So I have a 2 games running side by side.
proj2/main.cpp (main that gets run that then can call either game)
proj2/spades/display.cpp
proj2/spades/gameplay.cpp
proj2/spades/otherFiles.cpp
proj2/hearts/display.cpp (this is the same display class as in the spades game with the same functions just sightly altered.)
proj2/hearts/hearts.cpp
proj2/hearts/otherFiles.cpp
I'm getting the following linker error,
./gamePlay.o: In function `__static_initialization_and_destruction_0(int, int)':
gamePlay.cpp:(.text+0x396): undefined reference to `spades::display::display()'
./gamePlay.o: In function `__tcf_3':
gamePlay.cpp:(.text+0x480): undefined reference to `spades::display::~display()'
in my gamePlay.cpp file I define the following and I think this is where my error is,
display monitor;
if I add the extern identifier to the line such as
extern display monitor;
the error will disappear only to produce the following issue,
./gamePlay.o: In function `spades::gamePlay::storeBid(std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >&)':
gamePlay.cpp:(.text+0x14ee): undefined reference to `spades::monitor'
gamePlay.cpp:(.text+0x14f3): undefined reference to `spades::display::drawBox(int, int, int, int, int)'
I think this error is this coming from these following lines, but I have no idea how to fix this error. Feeling quite lost at the moment. (I don't understand why I would need the extern as an identifier for monitor either). Also all the includes should be alright.
int key = monitor.captureInput();
monitor.drawBox(2, 5, 3, 2, 0);
Below is my display.h file
/*
* Description: Defines constants and class prototypes and variables
*/
// These are include files they are used for accessing functions and
// variables in external libraries.
#include <ncursesw/ncurses.h>
#include <iostream>
#include <cstdio>
// This is the namespace std, without this you would need to add "std::"
// infront of every variable and function from the std library.
//using namespace std;
// These Mouse Mask Definitions are for some of the common mouse buttons
// that are used in this project.
#define LEFT_UP 1
#define LEFT_DOWN 2
#define LEFT_CLICK 4
#define LEFT_DOUBLECLICK 24
#define RIGHT_UP 4096
#define RIGHT_DOWN 8192
#define RIGHT_CLICK 16384
#define RIGHT_DOUBLECLICK 32768
#define MIDDLE_ROLLUP 1048576
#define MIDDLE_ROLLDOWN 256
namespace spades {
// Some extended character definitions for showing the special symbols
// in the extended UTF-8 character map.
const char joker[] = {0xE2, 0x98, 0xBA, 0};
const char clubs[] = {0xE2, 0x99, 0xA3, 0};
const char diamonds[] = {0xE2, 0x99, 0xA6, 0};
const char hearts[] = {0xE2, 0x99, 0xA5, 0};
const char spades[] = {0xE2, 0x99, 0xA0, 0};
/*
* This is the display class definitions
*/
class display {
public:
/* "constructor"
* This function is called whenever a new object of class display is first
* created. Put initialization functions in here.
*/
display(void){};
/* "destructor"
* This function is called just before a object is deleted. Put clean up
* functions in here.
*/
~display(){}; // destructor
// captures user input
int captureInput(void);
// stores new screensize on update
void handleResize(int sig);
/*
* Drawing commands
*/
// display a card on the screen
void displayCard(int x, int y, int suit, int number, int printAtt);
// erase in the shape of a box on the screen
void eraseBox(int x, int y, int sizeX, int sizeY);
// draw a box on the screen
void drawBox(int x, int y, int sizeX, int sizeY, int printAtt);
// display banner text at the bottom of the screen
void bannerBottom(std::string bannerText);
void bannerAboveBottom(std::string bannerText);
// display banner text at the top of the screen
void bannerTop(std::string bannerText);
// get information about the display
int getCols(void) {return cols;}
int getLines(void) {return lines;}
int getMouseEventX(void) {return mouseEventX;}
int getMouseEventY(void) {return mouseEventY;}
int getMouseEventButton(void) {return mouseEventButton;}
// Updates the screen after you finish drawing
void updateScreen(void) {refresh();}
// sets an offset for when cards clip the bottom of the screen
void setBottomOffset(int offset) {lineBoundaryOffset=offset;}
private:
// These are private functions and variables used inside of display.
// You should not try to access these from outside the display class.
void printFace(int suit, int number, int line, int printAtt);
void printSuit(int suit);
void printNumber(int number);
int cols;
int lines;
int mouseEventX;
int mouseEventY;
int mouseEventButton;
int lineBoundaryOffset;
};
}
Below is my display.cpp file
/*
* Description: Defines the functionality of the user interface.
*
* NOTES:
* * Requires the terminal (Putty) to be set to UTF-8.
* * Does not function when running a screen session.
*/
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include "display.h"
namespace spades {
//using namespace std;
/* Function: This is the constructor.
* Description: It is called whenever an object of class display is initialized
*/
display::display(void) {
/* Initilization of Terminal */
// required to get card suits displaying, combined with UTF-8 set in terminal
setlocale(LC_ALL,"en_US.utf-8");
// initialize ncurses screen
initscr();
// allow for color settings
start_color();
// clear the screen
clear();
// turn off the keyboard echo (reqiured while drawing)
noecho();
// Change to character mode (so individual characters are being read at a
// time rather than waiting for a carriage return).
cbreak();
// Allows for function keys to be used (also nessacary for getting the mouse
// movement working).
keypad(stdscr, TRUE);
// set which mouse events are captured
mousemask(ALL_MOUSE_EVENTS, NULL);
// Setting the timeout for the capture input values are in 1/10ths of a second.
halfdelay(5);
// setup the screen size settings.
cols = 80;
lines = 24;
// this updates the locally stored col and line variables
handleResize(0);
// set a no card draw offset of 1 so the bottom banner is not overwritten
lineBoundaryOffset = 1;
// Settings for card colors (these can be set outside of the display class)
init_pair(1, COLOR_CYAN, COLOR_BLACK); // for card outline
init_pair(2, COLOR_BLUE, COLOR_BLACK); // for spades and clubs
init_pair(3, COLOR_RED, COLOR_BLACK); // for hearts and diamonds
init_pair(4, COLOR_GREEN, COLOR_BLACK); // for turned over card
init_pair(5, COLOR_GREEN, COLOR_BLACK); // for box drawing
init_pair(6, COLOR_GREEN, COLOR_BLACK); // for banner display
}
/* Function: This is the destructor.
* Description: This is called just before an object is deleted.
*/
display::~display() {
// this is turns off all the special settings and returns the terminal to normal
endwin();
// insert deletion of dynamically created objects here too
}
/*
* Function: This captures all the userinput.
* Description: It captures mouse and keyboard events.
* Returns "Positive Number"
* - for user keypress
* - this is a character code typed
* Returns "0" - for no user input
* - this is when nothing is typed for half a second
* - allows for other timed operations to occur
* Returns "-1" - for mouse event
* - details of the mouse event must be fetched from this class
* - use getMouseEventX, getMouseEventY and getMouseEventButton
*/
int display::captureInput(void) {
// obtain one mouse event or keypress
int ch=getch();
// this is a switch statement for the result of getch
switch (ch) {
case KEY_MOUSE: // this occurs when an mouse event occurs
{
MEVENT mevent; // this is a variable declared of type MEVENT
getmouse(&mevent); // this gets the mouse event from ncurses (library)
mouseEventX = mevent.x; // get the column location of the event
mouseEventY = mevent.y; // get the row location of the event
mouseEventButton = mevent.bstate; // get the button state of the mouse
return -1; // -1 is for a mouse event
}
break;
case ERR: // this occurs when there is a timeout
{
return 0; // 0 is when nothing occurs
}
break;
default: // this occurs when a key is pressed
return ch; // a character is when the user types something
}
return 0; // this is never called, but is used to stop compiler complaints
}
/*
* Function: Updates all the information in the display class on window resize
* Description: This function just updates information, it requires the call
* from a static singal handler. Signal handlers service interrupts and require
* a static function to be called, because they are not part of the main
* program control flow. The signal handler should be declared in the main
* class, because your game should redraw the display on a resize.
*/
void display::handleResize(int sig) {
#ifdef TIOCGSIZE // operating system dependant differences
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts); // get the information of the terminal
cols = ts.ts_cols;
lines = ts.ts_lines;
#elif defined(TIOCGWINSZ)
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); // get the information of the terminal
cols = ts.ws_col;
lines = ts.ws_row;
#endif /* TIOCGSIZE */
resizeterm(lines, cols); // sets the ncurses window size correctly
}
/*
* Function: Displays various cards on the game screen
* Description: This function displays various playing cards on the screen.
* The first two arguments are the x and y coordinates of the top left corner
* of the card.
* The suit values are: 1=spades, 2=hearts, 3=clubs, 4=diamonds
* The numbers are: 1=Ace, 2-10=2-10, 11=Jack, 12=Queen, 13=King, 14=Joker
* Any suit and number that do not match the valid numberrs generates a face down
* card.
* The printAtt allows for one or more any of the following display settings:
* A_NORMAL Normal display (no highlight)
* A_STANDOUT Best highlighting mode of the terminal.
* A_UNDERLINE Underlining
* A_REVERSE Reverse video
* A_BLINK Blinking
* A_DIM Half bright
* A_BOLD Extra bright or bold
* A_PROTECT Protected mode
* A_INVIS Invisible or blank mode
* A_ALTCHARSET Alternate character set
* A_CHARTEXT Bit-mask to extract a character
* COLOR_PAIR(n) Color-pair number n
*/
void display::displayCard(int x, int y, int suit, int number, int printAtt) {
// Ncurses drawing settings
attron(COLOR_PAIR(1) | printAtt);
// prevent draw if it off the screen
if (x>=0 && y>=0 && x<cols-6 && y<lines-lineBoundaryOffset) {
// print the top lines of the card
mvprintw(y,x,"\u250c\u2500\u2500\u2500\u2500\u2510");
// the next 4 if statements prevent draw if it is over the bottom of the screen
if (y<lines-1-lineBoundaryOffset) {
move(y+1,x); // move command
printFace(suit,number,0, printAtt); // call function to print card face
}
if (y<lines-2-lineBoundaryOffset) {
move(y+2,x); // move command
printFace(suit,number,1, printAtt); // call function to print card face
}
if (y<lines-3-lineBoundaryOffset) {
move(y+3,x); // move command
printFace(suit,number,2, printAtt); // call function to print card face
}
if (y<lines-4-lineBoundaryOffset) {
// prints the bottom lines of the card
mvprintw(y+4,x,"\u2514\u2500\u2500\u2500\u2500\u2518");
}
}
// Ncurses turn off the drawing settings
attroff(COLOR_PAIR(1) | printAtt);
}
/*
* Function: Print a single line of what is written on the card.
* Description: This copies suit, number and printAtt from the calling function.
* Also includes what line of the card face is being drawn.
*/
void display::printFace(int suit, int number, int line, int printAtt) {
// draw left edge of the card
printw("\u2502");
if (suit==2 || suit==4) { // Red for Hearts and Diamonds
attron(COLOR_PAIR(3) | printAtt);
} else { // Black for Spades and Clover
attron(COLOR_PAIR(2) | printAtt);
}
// this the display of the joker
if (number==14) {
if (line==0)
printw("J%s ", joker);
if (line==1)
printw("oker");
if (line==2)
printw(" J%s", joker);
// this is the display for the cards with suits and numbers
} else if (suit>=1 && suit <=4 && number>=1 && number<=13) {
if (line==0) {
printSuit(suit); // function to draw suit
printNumber(number); // function to draw number
if (number!=10)
printw(" ");
printw(" ");
} else if (line==2) {
if (number!=10)
printw(" ");
printw(" ");
printNumber(number); // function to draw number
printSuit(suit); // function to draw suit
} else {
printw(" ");
}
// this is for a face down card
} else {
// the face down cards have a special color
attron(COLOR_PAIR(4) | printAtt);
if (line==0)
printw("%s %s", spades, hearts);
if (line==1)
printw("Play");
if (line==2)
printw("%s %s", diamonds, clubs);
attroff(COLOR_PAIR(1) | printAtt);
}
// turn on the card edge color settings
attron(COLOR_PAIR(1) | printAtt);
// print the right edge of the card
printw("\u2502");
}
/*
* Function: Print the suit of the card
* Description: This is just a look up table.
*/
void display::printSuit(int suit) {
switch (suit) {
case 1:
printw("%s",spades);
break;
case 2:
printw("%s",hearts);
break;
case 3:
printw("%s",clubs);
break;
case 4:
printw("%s",diamonds);
break;
default:
printw(" ");
break;
}
}
/*
* Function: Prints the number on the card
* Description: This is just a look up table.
*/
void display::printNumber(int number) {
switch (number) {
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
printw("%i",number);
break;
case 1:
printw("%c",'A');
break;
case 11:
printw("%c",'J');
break;
case 12:
printw("%c",'Q');
break;
case 13:
printw("%c",'K');
break;
default:
printw(" ");
break;
}
}
/*
* Function: Erases a rectangle on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is.
*/
void display::eraseBox(int x, int y, int sizeX, int sizeY) {
std::string strDraw;
int yCount;
int maxSizeX;
// this limits the column size of the draw when it is over the edge
// of the drawing area
if (sizeX+x > cols)
maxSizeX=cols-x;
else
maxSizeX=sizeX;
// for the number of rows that need to be drawn
for (yCount=0; yCount<sizeY;yCount++) {
// if the box goes over the edge of the drawable screen
// stop drawing by breaking the loop
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// reset the line to be drawn
strDraw = "";
// check that x is not off the screen
if (x<=cols && x >= 0) {
// make a string needed for box width
strDraw.append(maxSizeX,' ');
// print the line of the box
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
}
/*
* Function: Draws a box on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is. printAtt allows for changes in the
* display settings.
*/
void display::drawBox(int x, int y, int sizeX, int sizeY, int printAtt) {
std::string strDraw;
int ii;
int yCount;
// set the box setting colors on
attron(COLOR_PAIR(5) | printAtt);
// for the box height being drawn loop
for (yCount=0; yCount<sizeY;yCount++) {
// break loop if the drawing is offscreen
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// if x is on the screen
if (x<=cols) {
strDraw = "";
// for the box width loop
for (ii=0;ii<sizeX;ii++){
// stop drawing if the x is offscreen
if (ii+x > cols || x < 0)
break;
// first line
if (yCount==0) {
if (ii==0) {
strDraw.append("\u250c"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2510"); // right
} else {
strDraw.append("\u2500"); // middle
}
// last line
} else if (yCount==sizeY-1) {
if (ii==0) {
strDraw.append("\u2514"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2518"); // right
} else {
strDraw.append("\u2500"); // middle
}
// other lines
} else {
if (ii==0) {
strDraw.append("\u2502"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2502"); // right
} else {
strDraw.append(" "); // middle
}
}
}
// print the line that was created
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
// turn off the attribute colors
attroff(COLOR_PAIR(5) | printAtt);
}
/*
* Function: Draws a banner of text at the bottom right of the screen
* Description: Inverts the color and draws the banner at the bottom
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-1,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-1,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-1,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
void display::bannerAboveBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-2,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-2,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-2,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
}
/*
* Function: Draws a banner of text at the top left of the screen
* Description: Inverts the color and draws the banner at the top
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerTop(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(0,0);
// prints out the banner text
printw("%s", bannerText.c_str());
// fill in extra space after the banner text
hline(' ',cols - bannerText.size());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(0,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
}
Below is my gameplay.h file
#include "player.h"
#include "display.h"
#ifndef gamePlay_H
#define gamePlay_H
namespace spades {
class gamePlay{
bool spadesBroken;
public:
vector <spades::card> getDeck();
vector <spades::player>getplayers();
bool getSpadesBroken() {return spadesBroken;}
void setSpadesBroken(bool b){spadesBroken = b;}
int compareCenter(int leadplayer);
void createDeck();
void deal(vector <spades::card> &deck, vector <spades::player> &players);
void handSort();
bool containSuit(spades::card lead, spades::player players);
bool onlySpade(spades::player play);
int handCheck(int xevent, int yevent, vector <spades::player> players, int trickStart);
void displayHand();
void displayAdd();
void humanPlay(int trickStart);
void CPUplay(int trickStart, int CPU);
void score(spades::player &play, spades::player &play2);
void storeBid(stringstream &msg);
void runGame();
};
}
#endif
Below is my gameplay.cpp file
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include <fstream>
#include "gamePlay.h"
#include <cstdlib>
#include <sstream>
#include <ctime>
namespace spades {
vector <spades::player> players(4);
vector <spades::card> deck(52);
display monitor;
card center[4];
vector <spades::card> gamePlay::getDeck(){return deck;}
vector <spades::player> gamePlay::getplayers(){return players;}
//sorts the cards in the players hand into diamonds, clubs, hearts, spades
void gamePlay::handSort(){
int spades[13];
int hearts[13];
int clubs[13];
int diamonds[13];
int index;
int i;
for(i=0; i<13; i++){ //determines the card number and places them into corresponding indexes
index = (players.at(0).hand.at(i).getCardNum()+11)%13; //cause the cards to be placed based on their number with 2 being index 0 and 1(ace) being index 12
switch(players.at(0).hand.at(i).getSuit())
{
case 1: spades[index] = 1;
break;
case 2: hearts[index] = 1;
break;
case 3: clubs[index] = 1;
break;
case 4: diamonds[index] = 1;
break;
default: mvprintw(3,2,"!!!!!!!we have a problem!!!!!!!!!!");
break;
}
}
i = 0;
while(i<13){ //being placing the ordered cards back into the players hand
int j = 0;
while(j < 13){
if(diamonds[j] == 1){ //if a card has been placed in this index for the diamonds only array
if(j+2 == 14) //if the card is an ace
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2); //add 2 to each index to get the card number
players.at(0).hand.at(i).setSuit(4);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(clubs[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(3);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(hearts[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(2);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(spades[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(1);
i++;
}
j++;
}
}
}
//compares the center pile of 4 played cards to determine who wins the trick
int gamePlay::compareCenter(int leadplayer){
int highest = center[leadplayer].getCardNum();
if(center[leadplayer].getCardNum() == 1)
highest = 14;
int suit = center[leadplayer].getSuit();
int player = leadplayer;
for(int i = leadplayer+1; i < leadplayer+4; i++)
{
if(center[i%4].getSuit() == 1)
setSpadesBroken(true);
if((suit != 1) && (center[i%4].getSuit() == 1))
{
player = i%4;
suit = 1;
highest = center[i%4].getCardNum();
}
if(suit == center[i%4].getSuit()){
if(center[i%4].getCardNum() == 1){
player = i % 4;
highest = 14;
}
if(highest < center[i%4].getCardNum())
{
player = i%4;
highest = center[i%4].getCardNum();
}
}
}
players.at(player).setTricksTaken(players.at(player).getTricksTaken()+1); //increments the trick count of the winning player
return player; //return the player who won to determine who goes first next turn
}
//Create the deck of 52 cards by setting the suit and number of each card to a nonzero integer
void gamePlay::createDeck() {
for(int j = 0; j < 52; j++)
{
deck.at(j).setCardNum((j%13)+1);
deck.at(j).setSuit((j/13)+1);
}
random_shuffle(deck.begin(), deck.end());
}
//deal out 13 cards to each player by setting the
void gamePlay::deal(vector <spades::card> &newdeck, vector <spades::player> &newplayers){
for(int i = 0; i<52; i++){
newplayers.at(i/13).addCard(newdeck.at(i));
newdeck.at(i).setSuit(0);
newdeck.at(i).setCardNum(0);
}
}
//determines if the player still has a card of the same suit in their hand as the leading card played
bool gamePlay::containSuit(spades::card lead, spades::player players){
bool suit = false;
for(int i = 0; i < players.getHand().size(); i++){
if(lead.getSuit() == players.getHand().at(i).get
You have to write default constructor and destructor like this:
struct display
{
display() { }
~display() { }
// ^^^^
};
and not like this:
struct display
{
display();
~display();
};
However, if you do have the latter form in your header and want to keep it that way, you need to add definitions to the source file:
display::display() { }
display::~display() { }
The linker is complaining that it can't find object spades::monitor and function spades::display::drawBox(int, int, int, int). Are these defined anywhere? Please note that when defining a member variable/member function in a .cpp file (or anywhere outside of its class definition), you have to qualify it with the class name. Example:
In .h
namespace spades {
class display {
static display monitor;
void drawBox(int, int, int, int);
};
}
In .cpp
namespace spades {
display display::monitor;
void display::drawBox(int, int, int, int) {
//...
}
}
It's hard to say more without seeing the full code (at least the definitions of the things the linker is complaining about).
Related
I'm having some problems putting the finishing touches on the latest project. The Idea is to have an Infrared receiver mounted on a single Arduino Uno, taking ques from a remote to select preprogramed patterns on a Neopixel strip (selection from the Neopixel Strand test)
Here is my code
//Always comment your code like a violent psychopath will be maintaining it and they know where you live
#include <Adafruit_NeoPixel.h> //Neopixel Library
#include <IRLibAll.h> //IR reciever Library
IRrecvLoop myReceiver(2); //IR receiver on IO pin 2
IRdecode myDecoder; //Decoder object
unsigned long oldCode = 00000000; //Comparison variable to evaluate the execution of the 'Check' function later
Adafruit_NeoPixel strip (3,3,NEO_RGBW + NEO_KHZ800); //Creates the Pixel strip as an object in the code
void setup() {
strip.begin(); //Initialise the Neopixel strip
strip.show(); //Initialise all pixels to 'off'
myReceiver.enableIRIn(); // Start the IR receiver
}
void loop() {
check(); //Run the check function
delay(20); //Delay before running the loop again by 20 milliseconds giving time to recieve signals
}
void check() { //check Function: Checks for an IR signal before nominating which of the test displays to run
if (oldCode = myDecoder.value){ //Evaluates if the IR code recieved from the remote matches 'oldCode' and if it does....
return; //Terminates the check Function returning its values
}
if (myReceiver.getResults()) {
myDecoder.decode();
if (myDecoder.protocolNum == NEC) {
switch(myDecoder.value) { //Activate this switch statement based on the value 'myDecoder' holds
case 0xFFA25D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE21D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF629D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF22DD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF02FD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFC23D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE01F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFA857: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF906F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF9867: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFB04F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF6897:
colorWipe(strip.Color( 0, 0, 0), 50); // Black/off "0"
Serial.println("0 - Black/off");
break;
case 0xFF30CF:
colorWipe(strip.Color(255, 0, 0), 50); // Red "1"
Serial.println("1 - All red");
break;
case 0xFF18E7:
colorWipe(strip.Color( 0, 255, 0), 50); // Green "2"
Serial.println("2 - All green");
break;
case 0xFF7A85:
colorWipe(strip.Color( 0, 0, 255), 50); // Blue "3"
Serial.println("3 - All blue");
break;
case 0xFF10EF:
theaterChase(strip.Color(127, 127, 127), 50); // White "4"
Serial.println("4 - All white");
break;
case 0xFF38C7:
theaterChase(strip.Color(127, 0, 0), 50); // Red "5"
Serial.println("5");
break;
case 0xFF5AA5:
theaterChase(strip.Color( 0, 0, 127), 50); // Blue "6"
Serial.println("6");
break;
case 0xFF42BD:
rainbow(10); // "7"
Serial.println("7");
break;
case 0xFF4AB5:
theaterChaseRainbow(50); // "8"
Serial.println("8");
break;
case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFFFFFFF: Serial.println("Please release button and reselect"); break;
}
}
oldCode = myDecoder.value; //make the new button state equal the old buttonstate preventing the button from activating if statement
}
}
void colorWipe(uint32_t color, int wait) { //Colour wipe test
while(true) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
check();
delay(wait); // Pause for a moment
}
}
}
void theaterChase(uint32_t color, int wait) { //Theatre Chase test
while(true) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
for(int c=b; c<strip.numPixels(); c += 3) { // 'c' counts up from 'b' to end of strip in steps of 3...
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
check(); //Run the check function
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
}
void rainbow(int wait) { //Rainbow test function
while(true) { //while this function is active
for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) { //Sets differing colours for the rainbow
for(int i=0; i<strip.numPixels(); i++) { //For each pixel in strip...
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels()); //balances the colour pattern along the length of the strip
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue))); //run the colour pattern along the strip
check(); //run the check function
}
strip.show(); //Update strip with new contents
delay(wait); //Pause for a moment
}
}
}
void theaterChaseRainbow(int wait) {
while(true) {
int firstPixelHue = 0; //First pixel starts at red
for(int a=0; a<30; a++) { //Repeat 30 times...
for(int b=0; b<3; b++) { //'b' counts from 0 to 2...
strip.clear(); //Set all pixels to off
for(int c=b; c<strip.numPixels(); c += 3) { //'c' counts up from 'b' to end of strip in increments of 3, hue of pixel 'c' is offset by an amount to make one full revolution of the color wheel (range 65536) along the length of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels(); //create the hue variable and balance the rainbow colouring across the strip
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
check(); //Run the check function
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
}
The Problems
I have tried to write a loop that uses a class call to decide what is going on ("check"), I have also written another version where the switch statement is place in the loop instead. The problem from what I understand is that the IR input cannot receive whilst the loop is running (as it cannot interrupt) yet the test patterns will not play unless the loop is running. I have looked at solutions that involve using two Arduino's with either one running IR and the Neopixel respectively but unfortunately this will not match up with my brief - I need to have it running on one Arduino.
Any ideas?
For those who are interested - the final code to this project was...
//Always comment your code like it will be maintained by a violent psychopath who knows where you live.
#include <IRLibAll.h> //Infra Red Library
#include <Adafruit_NeoPixel.h> //Adafruit NeoPixel Library
//======== Constants =============
const int LEDpin = 3; //IO pin for the LED strip
const int LEDcount = 3; //Number of LED's in the strip
const int IRreceiver = 2; //IO pin for the IRreceiver
IRrecvPCI myReceiver(IRreceiver); //Instantiate the Infra Red receiver
IRdecode myDecoder; //Instatiate a Decoder object (Veriable to hold the recieved data from the button press)
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, color_WIPE, SCANNER, FADE }; //Limit the results 'pattern' will accept with an enumeration
//======== Variables =============
//=====Classes and Functions =====
class neoPatterns : public Adafruit_NeoPixel //A class to govern the operation of the Neopixel patterns outside of the Main loop
{
private:
int steps;
uint32_t color;
public:
pattern activePattern; //Tracks the pattern that is currently active on the strip
unsigned long interval; //Milliseconds between updates
unsigned long lastUpdate; //Records the millisecond of the last update
uint32_t color1, color2; //Variables for recording active colors
uint16_t totalSteps; //How many steps of the pattern have been called
uint16_t index; //What step within the pattern we are on
void (*onComplete)(); //onComplete callback function - still wondering how much i need this?
neoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) //Class constructor to...
:Adafruit_NeoPixel(pixels, pin, type) //initialise the Neopixel strip
{
onComplete = callback;
}
void update() //Function that manages updating the pattern
{
if((millis() - lastUpdate) > interval) //Is it time to update?
{
lastUpdate = millis(); //Updates 'lastUpdate' to the current milli's value
switch(activePattern) //Switch statement to track which pattern needs its update function
{
case RAINBOW_CYCLE: //If rainbowCycle...
rainbowCycleUpdate(); //update rainbowCycle
break;
case THEATER_CHASE: //If theatreChase...
theaterChaseUpdate(); //update theatreChase
break;
case color_WIPE: //if colorWipe
colorWipeUpdate(); //update colorWipe
break;
case SCANNER: //if scanner
scannerUpdate(); //update scanner
break;
case FADE: //if fade
fadeUpdate(); //update fade
break;
default:
break;
}
}
}
void increment() //Function for incrementing values to drive strand tests
{
index++; //increment index variable
if (index >= totalSteps) //if index is greater than or equal to totalsteps...
{
index = 0; //..reset index to 0 and...
if (onComplete != NULL) //... if onComplete has no value...
{
onComplete(); //...call the onComplete callback
}
}
}
void rainbowCycle(uint8_t interval) //Rainbow Cycle strand test pattern
{
activePattern = RAINBOW_CYCLE; //Set current active pattern to Rainbow Cycle...
interval = interval; //reset interval to interval
totalSteps = 255; //set total step variable to 255
index = 0; //set index variable to 0
}
void rainbowCycleUpdate() //update for Rainbow Cycle
{
for(int i=0; i< numPixels(); i++) //create a variable called 'i' which is equal to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
setPixelColor(i, wheel(((i * 256 / numPixels()) + index) & 255)); //set the pixel color to ...
}
show(); //update the orders to the Neopixel strand
increment(); //Run the increment function
}
void colorWipe (uint32_t color, uint8_t interval) //color wipe funtion
{
activePattern = color_WIPE; //update the current active pattern to color Wipe
interval = interval; //reset the interval variable
totalSteps = 255; //set the total steps variable to 255
color1 = color; //set color to color 1
index = 0; //reset the index variable to 0
}
void colorWipeUpdate() //Color wipe update function
{
setPixelColor(index, color1); //change the pixel color to color1
show(); //update the strand
increment(); //run the increment function
}
void theaterChase(uint32_t color1, uint32_t color2, uint8_t interval) //Theatre Chase funtion
{
activePattern = THEATER_CHASE; //change the current active pattern to Theatre Chase
interval = interval; //reset the interval variable
totalSteps = numPixels(); //update the total steps variable to be equivilent to the number of pixels
color1 = color1; //Reset color1
color2 = color2; //Reset color2
index = 0; //Set index variable to 0
}
void theaterChaseUpdate() //Theatre Chase update function
{
for(int i=0; i< numPixels(); i++) //take the i variable and reset it to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
if ((i + index) % 3 == 0) //if the total of I and index divide equally by 3...
{
setPixelColor(i, color1); //...set the pixelcolor to color 1...
}
else //...otherwise...
{
setPixelColor(i, color2); //set the pixel color to color 2
}
}
show(); //update the neopixel strand
increment(); //run the increment function
}
void scanner(uint32_t color1, uint8_t interval) //Scanner function
{
activePattern = SCANNER; //update the active pattern to Scanner
interval = interval; //reset the interval variable
totalSteps = (numPixels() - 1) * 2; //set the total steps variable to by equal to twice that of the number of pixels on the strand less one
color1 = color1; //reset the color1 variable
index = 0; //set the index variable to 0
}
void scannerUpdate() //Scanner update function
{
for (int i = 0; i < numPixels(); i++) //take the i variable and reset it to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
if (i == index) //if the i variable is equivilant to the index variable...
{
setPixelColor(i, color1); //set the pixel color to color1
}
else if (i == totalSteps - index) //if the i variable is equivilant to totalsteps less the value of index...
{
setPixelColor(i, color1); //set the pixel color to color1...
}
else //otherwise...
{
setPixelColor(i, DimColor(getPixelColor(i))); //dim the current pixel value
}
}
show(); //update the strand
increment(); //run the increment function
}
void fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval) //Fade function
{
activePattern = FADE; //set the current active pattern to fade
interval = interval; //reset the interval variable
totalSteps = steps; //create a new steps variable and set it to be eqivilant to totalSteps
color1 = color1; //reset color1
color2 = color2; //reset color2
index = 0; //set index to 0
}
void fadeUpdate() //Fade update function
{
uint8_t red = ((Red(color1) * (totalSteps - index)) + (Red(color2) * index)) / totalSteps;
uint8_t green = ((Green(color1) * (totalSteps - index)) + (Green(color2) * index)) / totalSteps;
uint8_t blue = ((Blue(color1) * (totalSteps - index)) + (Blue(color2) * index)) / totalSteps;
colorSet(Color(red, green, blue));
show(); //update the strand
increment(); //run the increment function
}
uint8_t Red(uint32_t color) //Red color function
{
return (color >> 16) & 0xFF;
}
uint8_t Green(uint32_t color) //Green color function
{
return (color >> 8) & 0xFF;
}
uint8_t Blue(uint32_t color) //Blue color function
{
return color & 0xFF;
}
uint32_t DimColor(uint32_t color) //color dimming function
{
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}
uint32_t wheel(byte wheelPos) //color wheeling function for the rainbow color functions
{
wheelPos = 255 - wheelPos;
if(wheelPos < 85)
{
return Color(255 - wheelPos * 3, 0, wheelPos * 3);
}
else if(wheelPos < 170)
{
wheelPos -= 85;
return Color(0, wheelPos * 3, 255 - wheelPos * 3);
}
else
{
wheelPos -= 170;
return Color(wheelPos * 3, 255 - wheelPos * 3, 0);
}
}
void colorSet(uint32_t color) //color set function sets all colors to the same synchronus color
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}
void IRSelector() //Infra Red selection function - takes action based on IR code received
{
if (myDecoder.protocolNum == NEC) { //ignore any code that is not recieved from a NEC remote control
switch(myDecoder.value) //Switch statement that makes a decision based upon the value recieved from the Infra Red decoder
{
case 0xFFA25D: Serial.println("Untethered button, please select from 0-8"); break; //=====================================================================
case 0xFFE21D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF629D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF22DD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF02FD: Serial.println("Untethered button, please select from 0-8"); break; // ------------- UNASSIGNED BUTTON SELECTIONS -------------------------
case 0xFFC23D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE01F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFA857: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF906F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF9867: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFB04F: Serial.println("Untethered button, please select from 0-8"); break; //=====================================================================
case 0xFF6897: //"0 - All black (off)"
colorWipe(color, interval);
Serial.println("0 - Black/off");
break;
case 0xFF30CF: //"1 - All red"
colorWipe(color, interval);
Serial.println("1 - All red");
break;
case 0xFF18E7: //"2 - All green"
colorWipe(color, interval);
Serial.println("2 - All green");
break;
case 0xFF7A85: //"3 - All blue"
colorWipe(color, interval);
Serial.println("3 - All blue");
break;
case 0xFF10EF: //"4 - All white"
colorWipe(color, interval);
Serial.println("4 - All white");
break;
case 0xFF38C7: //"5 - Rainbow Cycle"
rainbowCycle(interval);
Serial.println("5");
break;
case 0xFF5AA5: //"6 - Theater Chase"
theaterChase(color1, color2, interval);
Serial.println("6");
break;
case 0xFF42BD: //"7 - Scanner"
scanner(color1, interval);
Serial.println("7");
break;
case 0xFF4AB5: //"8 - Fader"
fade(color1, color2, steps, interval);
Serial.println("8");
break;
case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break; //button 9 - unassigned
case 0xFFFFFFFF: Serial.println("Please release button and reselect"); break; //consistant repeat code
default:
Serial.print(" other button ");
Serial.println(myDecoder.value);
}//End of Switch
}
}//End of IRSelector method
}; // End of neoPatterns class
void strandComplete();
neoPatterns strand(LEDcount, LEDpin, NEO_RGBW + NEO_KHZ800, &strandComplete); //Neopattern object to define the strand
void setup(){ /*----( SETUP: RUNS ONCE )----*/
Serial.begin(9600); //engage the serial monitor
Serial.println("IR Receiver Button Decode"); //print out to the monitor
myReceiver.enableIRIn(); //Start the receiver
strand.begin(); //start the Neopixel strip
}/*--(end setup )---*/
void loop(){ /*----( LOOP: RUNS CONSTANTLY )----*/
if (myReceiver.getResults()) //check to see if we have received an IR signal?
{
myDecoder.decode(); //Decode the recieved signal
strand.IRSelector(); //Run the IR selection function
myReceiver.enableIRIn(); //reset the receiver for a new code
}
strand.update(); //Run the update function on the Neopixel strand
}/* --(end main loop )-- */
void strandComplete()
{
// Random color change for next scan
strand.color1 = strand.wheel(random(255));
}`
Sorry for the massive block of code ;w;
I'm learning C++, and this is an assignment I'm stuck on- I'm supposed to be making a brick wall with alternating rows offset by 1/2 bricks, and I'm supposed to be using a nested loop to do so.
I've got the functions to draw the full brick and half bricks, and I've successfully been able to create a full line of 9 bricks(9 being how many bricks can span the window) and I'm stuck on the next step. I need to make the program draw the next line after the first one is finished, but "\n" and "cout << endl;" only affect the main window and not the "TurtleWindow" that OpenCV opens. All I have in that regard is that it has something to do with the "changePosition" command, but I'm not sure how to add that in to my loop. If I set position to some specific (x, y) coords, then wouldn't it just keep setting the "bricks" in that position every loop?
Any help would be appreciated, I feel like I'm so close to the solution but this is stumping me...
// BrickWall.cpp : This file contains the 'main' function. Program execution begins
//and ends there.
//
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdio.h>
#include <math.h>
using namespace cv;
using namespace std;
#define M_PI 3.14159265358979323846264338327950288
#define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
char wndname[] = "TurtleWindow";
Mat image = Mat::zeros(500, 500, CV_8UC3);
Scalar WHITE(255, 255, 255);
const int DELAY = 1;
Point _curPosition(250, 250);
int _direction = 0;
void init()
{
imshow(wndname, image);
waitKey(DELAY);
}
//
// Move the pen to the given coordinates without leaving a mark
//
// Note (0,0) refers to the upper left corner
// (500,500) refers to the bottom right corner
//
void changePosition(int x, int y)
{
_curPosition.x = x;
_curPosition.y = y;
}
//
// point in the direction given in degrees
// 0 ==> point right
// 90 ==> point down
// 180 ==> point left
// 270 ==> point up
//
void changeDirection(int direction)
{
_direction = direction;
}
//
// Moves the pen forward the given number of pixels
// Note leaves a mark creating a line from the previous point
// to the new point
//
void moveForward(int nPixels)
{
int x = static_cast<int>(round(nPixels * cos(degToRad(_direction))));
int y = static_cast<int>(round(nPixels * sin(degToRad(_direction))));
Point newPoint = Point(x + _curPosition.x, y + _curPosition.y);
line(image, _curPosition, newPoint, WHITE);
_curPosition = newPoint;
// cout << "moved to " << newPoint.x << "," << newPoint.y << endl;
imshow(wndname, image);
waitKey(DELAY);
}
void fullBrick()
// changePosition(25, 25);
{
changeDirection(0);
moveForward(50);
changeDirection(90);
moveForward(20);
changeDirection(180);
moveForward(50);
changeDirection(270);
moveForward(20);
changeDirection(0);
moveForward(50);
}
void halfBrick()
//changePosition(25, 45);
{
changeDirection(0);
moveForward(25);
changeDirection(90);
moveForward(20);
changeDirection(180);
moveForward(25);
changeDirection(270);
moveForward(20);
}
int main()
{
int counter;
int counterWall;
counterWall = 0;
counter = 0;
init();
changePosition(25, 25);
while (counter < 20)
{
do
{
changeDirection(0);
moveForward(50);
changeDirection(90);
moveForward(20);
changeDirection(180);
moveForward(50);
changeDirection(270);
moveForward(20);
changeDirection(0);
moveForward(50);
counterWall++;
} while (counterWall < 9);
counter++;
}
waitKey();
}
edit: thanks everyone for the advice! I was fully intending to use a nested loop in the end(and did use one, so no lost points there!) and was able to realize there was nothing stopping me from looping the bricks like a snake. I had to turn the assignment in before I could try any of the changes you guys offered, but I'll be sure to keep them in mind for future assignments!
and here's a link to the picture of the final product, I've never been happier to see a brick wall in my life
Brick Wall Picture
I am a beginner programmer working on a program in c++ visual studio 2015 that takes an instance of a class titled rect and passes it to a function within rect that sets a rectangle of random size and position somewhere on a imaginary board in a console window. At the bottom of the code there are full instructions on what the code needs to do. The problem I am having is when the program prints the rectangles, the rectangle of "0's" is not printing but the rectangle of "1's" is. The rectangle rect0 is being passed by reference and the rect1 is being passed by pointer.
/*
iLab2: rectangles
*/
#define NOMINMAX // prevent Windows API from conflicting with "min" and "max"
#include <stdio.h> // C-style output. printf(char*,...), putchar(int)
#include <windows.h> // SetConsoleCursorPosition(HANDLE,COORD)
#include <conio.h> // _getch()
#include <time.h>
/**
* moves the console cursor to the given x/y coordinate
* 0, 0 is the upper-left hand coordinate. Standard consoles are 80x24.
* #param x
* #param y
*/
void moveCursor(int x, int y)
{
COORD c = { x,y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
struct Vec2
{
short x, y; // variables x and y for storing rectangle coordinates
Vec2() : x(0), y(0) { } // default constructor for vect2 if no parameters are specified
Vec2(int x, int y) : x(x), y(y) { } // default constructor for vect2 if parameters are given
void operator+=(Vec2 v) // function for adding or subtracting (if v is negative) to move the rectangle
{
x += v.x;
y += v.y;
}
};
class Rect
{
Vec2 min, max;
public:
Rect(int minx, int miny, int maxx, int maxy)
:min(minx, miny), max(maxx, maxy)
{}
Rect() {}
void draw(const char letter) const
{
for (int row = min.y; row < max.y; row++)
{
for (int col = min.x; col < max.x; col++)
{
if (row >= 0 && col >= 0)
{
moveCursor(col, row);
putchar(letter);
}
}
}
}
void setMax(int maxx, int maxy)
{
this->max.x = maxx;
this->max.y = maxy;
}
void setMin(int minx, int miny)
{
this->min.x = minx;
this->min.y = miny;
}
bool isOverlapping(Rect const & r) const
{
return !(min.x >= r.max.x || max.x <= r.min.x
|| min.y >= r.max.y || max.y <= r.min.y);
}
void translate(Vec2 const & delta)
{
min+=(delta);
max+=(delta);
}
void setRandom(Rect & r);
void setRandom(Rect* r);
};
void Rect::setRandom(Rect & r)
{
srand(time(NULL)); // added to make the random placement and size of the rect different each time program runs
int pos_x, pos_y, height, width;
pos_x = rand() % 51;
pos_y = rand() % 21;
height = 2 + rand() % 11;
width = 2 + rand() % 11;
height = height / 2;
width = width / 2;
r.min.x = pos_x - width;
r.min.y = pos_y - height;
r.max.x = pos_x + width;
r.max.y = pos_y + height;
}
void Rect::setRandom(Rect * r)
{
srand(time(NULL)); // added to make the random placement and size of the rect different each time program runs
int posX, posY, heightPoint, widthPoint;
posX = rand() % 51;
posY = rand() % 21;
heightPoint = 2 + rand() % 11;
widthPoint = 2 + rand() % 11;
heightPoint = heightPoint / 2;
widthPoint = widthPoint / 2;
this->min.x = posX - widthPoint;
this->min.y = posY - heightPoint;
this->max.x = posX + widthPoint;
this->max.y = posY + heightPoint;
}
int main()
{
// initialization
//Rect userRect(7, 5, 10, 9); // (x-min, y-min, x-max, y-max) x-min how far left the rectange can be
//Rect rect0(10, 2, 14, 4); // (x-min, y-min, x-max, y-max)
//Rect rect1(1, 6, 5, 15); // (x-min, y-min, x-max, y-max)
//Rect userRect;
Rect * userRect;
Rect rect0;
Rect rect1;
const int rectSize = 5;
Rect rect[rectSize];
userRect = new Rect();
// set
rect[0].setRandom(rect[0]);
rect[1].setRandom(& rect[1]);
userRect->setMin(7, 5);
userRect->setMax(10, 9);
//rect0.setMin(10, 2);
//rect0.setMax(14, 4);
//rect1.setMin(1, 6);
//rect1.setMax(5, 15);
int userInput;
do
{
// draw
rect[0].draw('0'); // drawing the 0 rectangle with an x width of 4 and a y height of 2
rect[1].draw('1'); // drawing the 1 rectangle with a x width of 4 and a y height of 9
moveCursor(0, 0); // re-print instructions
printf("move with 'w', 'a', 's', and 'd'");
userRect->draw('#'); // drawing the user rectangle in its starting location with a x width of 3 and a y height of 4
// user input
userInput = _getch();
// update
Vec2 move;
switch (userInput)
{
case 'w': move = Vec2(0, -1); break; // Moves the user Rectangle -y or up on the screen
case 'a': move = Vec2(-1, 0); break; // Moves the user Rectangle -x or left on the screen
case 's': move = Vec2(0, +1); break; // Moves the user Rectangle +y or down on the screen
case 'd': move = Vec2(+1, 0); break; // Moves the user Rectangle +x or right on the screen
}
userRect->draw(' '); // un-draw before moving
userRect->translate(move); // moves the user rectangle to the new location
} while (userInput != 27); // escape key
delete userRect; // delete dynamic object to release memory
return 0;
}
// INSTRUCTIONS
// ------------
// 3) Random rectangles, by reference and by pointer
// a) create a method with the method signature "void setRandom(Rect & r)".
// This function will give the passed-in Rect object a random location.
// The random x should be between 0 and 50 x. The random y should be
// between 0 and 20. Limit the possible width and height to a minimum of 2
// and a maximum of 10.
// b) test "void setRandom(Rect & r)" on the local Rect object "rect0".
// c) create a method with the method signature
// "void setRandomByPointer(Rect * r)", which functions the same as
// "void setRandom(Rect & r)", except that the argument is
// passed-by-pointer.
// d) test "void setRandomByPointer(Rect * r)" on the local Rect object
// "rect1".
// 4) Test and show overlap
// a) Using the existing function "isOverlapping(Rect const &)", test to see
// if userRect collides with any other Rect objects. If userRect is
// overlapping, draw it with '+' instead '#'.
// b) Create a Rect * pointer that points to the address if the Rect object
// that userRect collides with. It should point at NULL if userRect is
// colliding with no other Rect objects.
// c) Print to the screen the width and height of a Rect object that userRect
// collides with. If no collision is happening, print "no collision"
// instead.
// 5) Array of objects
// a) Replace the Rect objects rect0 and rect1 with an array of 2 Rect
// objects, "rect[2]".
// b) Make sure you replace every remaining "rect0" with "rect[0]", and every
// "rect1" with "rect[1]".
// c) Increase the size of the "rect" array to 5. Make sure all 5 Rect
// objects are randomized, drawn to the screen, and tested for collision.
// d) If you have not already done so, replace
// duplicate-code-using-array-elements with a for-loop. For example:
// If you have:
// rect[0].draw('0');
// rect[1].draw('1');
// rect[2].draw('2');
// rect[3].draw('3');
// rect[4].draw('4');
// Replace it with:
// for(int i = 0; i < NUMBER_OF_RECTS; i++)
// {
// rect[i].draw('0'+i);
// }
// Do this where objects are randomized, drawn, and tested for collision
You have two different setRandom() methods with three problems.
Each time either setRandom() gets called, srand() also gets called. srand() should only be called once, when the program starts -- read the first answer to that question, carefully.
Code duplication. The code in both setRandom() is nearly identical. Code duplication is bad. Duplicated code means that if the algorithm needs to be changed in some way, you will have to remember to do it in two places. Or three places. Or four places. Or however many duplicate chunks of code exist in the code. You have to remember them all, and find them. If you miss one, bugs galore.
Same problem as #2, but for the "nearly identical" part. The difference is: the first version of setRandom() takes a reference to another object and modifies another object that's passed by reference. The second version of setRandom() takes a pointer to another object instead of a reference, but ignores it completely, and instead initializes this, instead of the pointed object.
And, as a result of these bugs, we get the results you're seeing.
rect[0].setRandom(rect0);
This ends up initializing rect0. rect[0] is ignored completely, and not initialized at all.
rect[1].setRandom(& rect1);
This ends up initializing rect[1]. rect1 is ignored completely, and not initialized at all.
And that's why the rest of the code fails to draw rect[0]. It does not get initialized at all.
The shown code is completely confused because it has four, and not two, objects. rect0, rect1, and the rect[] array containing two more objects. After they are declared, rect0 and rect1 are completely ignored, except for the misfired initialization, and they serve apparently no purpose whatsoever.
Neither is there any real reason here for setRandom() to take either a pointer or a reference to some other object. The apparent purpose of setRandom() is to initialize an object's dimensions randomly.
So it should simply initialize this's dimensions randomly. Passing some other object, by pointer or reference, makes no sense at all.
Then, after getting rid of rect0 and rect1, and simply calling a single setRandom() method...
rect[0].setRandom();
rect[1].setRandom();
... the rest of the code will proceed and properly draw two randomly-initialized objects.
the code Rect rect[ rectSize ] will create 5 rects to array rect and all of those rects are with min(0,0) max(0,0)(initial state). when you call rect[ 0 ].setRandom( rect0 ) which will update rect0(you pass it by reference) and do nothing to rect[0].when you call rect[ 1 ].setRandom( &rect1 ) you update rect[1] (by this->min.x = posX - some value).so you get difference between rect[0] and rect[1].
I am currently stuck trying to create a grid for my controllable snake to move around. Currently. I am using a resolution of 1024x768 and would like the snake to move between a 16x16 grid (64x48 resolution)
So far the snake just moves pixel by pixel at a set speed.
I'll paste the .cpp and .hpp files below which i think are relevant to where i need to implement the code. If anyone could provide any suggestions/code that would be great!
snake.cpp
#include "snake.hpp"
#include <cstdlib>
void Snake::move()
{
switch(direction_){
case Direction::North:
position_.y += 1;
break;
case Direction::East:
position_.x += 1;
break;
case Direction::South:
position_.y -= 1;
break;
case Direction::West:
position_.x -= 1;
}
if (position_.x < 0) position_.x = 63; else if (position_.x > 63) position_.x = 0;
if (position_.y < 0) position_.y = 47; else if (position_.y > 47) position_.y = 0;
}
void Snake::render(prg::Canvas& canvas) const
{
canvas.drawCircle(getPosition().x * 16, getPosition().y * 16,16,prg::Colour::WHITE);
}
void Snake::changeDirection(Direction new_direction)
{
direction_ = new_direction;
}
snake.hpp
#if !defined SNAKE_HPP
#define SNAKE_HPP
#include <prg_interactive.hpp>
enum class Direction {
North = 1, East, South, West
};
struct Position final {
int x{0}, y{0};
Position(int ix, int iy) : x{ix}, y{iy} {}
};
class Snake {
public:
virtual ~Snake() {}
virtual void move();
void render(prg::Canvas& canvas) const;
void changeDirection(Direction new_direction);
const Position& getPosition() const {return position_;}
void setPosition(const Position& position){ position_ = position;}
private:
Direction direction_ {Direction::North};
Position position_ {0,0};
};
class PlayerSnake : public Snake,
public prg::IKeyEvent {
public:
PlayerSnake();
virtual ~PlayerSnake();
bool onKey(const prg::IKeyEvent::KeyEvent& key_event) override;
};
#endif // SNAKE_HPP
play_state.cpp
#include "play_state.hpp"
#include "ai_snake.hpp"
#include "player_snake.hpp"
#include <iostream>
const size_t MaxShapes {5};
const unsigned int MaxScale {5};
bool PlayState::onCreate()
{
snakes_.push_back(new AISnake);
snakes_.back()->setPosition(Position(100,100));
snakes_.push_back(new PlayerSnake);
snakes_.back()->setPosition(Position(50,50));
double x, y;
for(unsigned shape = 0;shape < MaxShapes;shape++)
{
x = (double)(rand() % prg::application.getScreenWidth());
y = (double)(rand() % prg::application.getScreenHeight());
shapes_.push_back(Square({x, y}));
}
return true;
}
bool PlayState::onDestroy()
{
return true;
}
void PlayState::onEntry()
{
prg::application.addKeyListener(*this);
game_timer_.start();
}
void PlayState::onExit()
{
prg::application.removeKeyListener(*this);
game_timer_.stop();
}
void PlayState::onUpdate()
{
}
void PlayState::onRender(prg::Canvas& canvas)
{
const std::string text = "";
canvas.blitFast(
background_,
canvas.getWidth() / 2 - background_.getWidth() / 2,
canvas.getHeight() / 2 - background_.getHeight() / 2
);
prg::uint text_dims[2];
prg::Font::MASSIVE.computePrintDimensions(text_dims, text);
prg::Font::MASSIVE.print(
canvas,
prg::application.getScreenWidth() / 2 - text_dims[0] / 2,
prg::application.getScreenHeight() / 2 - text_dims[1] / 2,
prg::Colour::RED,
text);
for(const auto snake : snakes_) {
snake->render(canvas);
}
for(Shape shapes : shapes_) {
shapes.render(canvas);
}
}
bool PlayState::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_ESC_KEY:
prg::application.exit();
break;
}
}
return true;
}
void PlayState::onTimer(prg::Timer& timer)
{
for(auto snake : snakes_) {
snake->move();
}
}
play_state.hpp
#if !defined PLAY_STATE_HPP
#define PLAY_STATE_HPP
#include <prg_interactive.hpp>
#include "snake.hpp"
#include "square.hpp"
#include <list>
//Example of forward declaration of Snake class
class Snake;
class PlayState final : public prg::IAppState,
public prg::IKeyEvent,
public prg::ITimerEvent {
public:
PlayState() = default;
bool onCreate() override;
bool onDestroy() override;
void onEntry() override;
void onExit() override;
void onUpdate() override;
void onRender(prg::Canvas& canvas) override;
bool onKey(const prg::IKeyEvent::KeyEvent& key_event) override;
void onTimer(prg::Timer& timer) override;
private:
//Snake* snakes_[2] {nullptr,nullptr};
std::list<Snake*> snakes_;
prg::Timer game_timer_ {0, 1000 / 30, *this};
const prg::Image background_ {prg::ImageFile("resources/images/border.bmp").load()};
std::vector<Shape> shapes_;
};
#endif // PLAY_STATE_HPP
player_snake.cpp
#include "player_snake.hpp"
//PlayerSnake Implementation
//
PlayerSnake::PlayerSnake()
{
prg::application.addKeyListener(*this);
}
PlayerSnake::~PlayerSnake()
{
prg::application.removeKeyListener(*this);
}
bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_LEFT_KEY:
changeDirection(Direction::West);
break;
case KeyEvent::KB_RIGHT_KEY:
changeDirection(Direction::East);
break;
case KeyEvent::KB_UP_KEY:
changeDirection(Direction::North);
break;
case KeyEvent::KB_DOWN_KEY:
changeDirection(Direction::South);
break;
}
}
return true;
}
player_snake.hpp
#if !defined PLAYER_SNAKE_HPP
#define PLAYER_SNAKE_HPP
#include "snake.hpp"
#include <prg_interactive.hpp>
#endif //PLAYER_SNAKE_HPP
You snake is initially set out of bounds:
snakes_.push_back(new AISnake);
snakes_.back()->setPosition(Position(100,100));
snakes_.push_back(new PlayerSnake);
snakes_.back()->setPosition(Position(50,50));
Because in the move function, you limit the snake to (64, 48) as the maximum location. The x position 100 is past the limit. The y positions are past the limit.
You may want to set them at different locations based on the MAXIMUM_WIDTH and MAXIMUM_HEIGHT constants (see my comments):
Snake AISnake;
AISnake.setPosition(MAXIMUM_WIDTH / 4, MAXIMUM_HEIGHT / 4);
snakes_.push_back(AISnake);
Snake PlayerSnake;
PlayerSnake.setPosition(MAXIMUM_WIDTH * 3 / 4,
MAXIMUM_HEIGHT * 3 / 4);
snakes_.push_back(PlayerSnake);
Also note that in the above code fragment, there is no dynamic memory allocation. The variables (snakes) are defined locally and copied into the snakes_ container. This means that there is no need to worry about memory leaks or memory management (like when to delete the memory occupied by the snake).
More Out Of Bounds
Some places, you use the screen dimensions for the boundaries, others, you use a hard coded value:
x = (double)(rand() % prg::application.getScreenWidth());
y = (double)(rand() % prg::application.getScreenHeight());
You need to decide whether the game board occupies the entire screen dimensions or is a fixed size. If you keep the above statements, you should test the positions to verify they are within the game borders.
If this is a GUI application, you need to decide on a fixed size game board or a board set to the size of the window or expanding to the entire screen. For a fixed size board, you should consider a "viewport" design. This means that the window is a view or port showing a small portion of the board (the board is bigger than the window).
Separation of Screen Vs. Board
You should separate the concepts between a logical board and the physical screen. This concept lets you adapt the board to the screen without affecting any other modules.
For example, the Board draws onto the screen. A Board cell may have different pixel dimensions depending on the screen resolution. However, a part of the snake will always occupy at least one Board cell. So the snake movement doesn't depend on the screen resolution, only the Board dimensions.
By placing the objects, Board and Screen, in separate files, you can change the screen dimensions without having to recompile any of the snake or main files.
I'm writing a video poker game and I'm having a problem with a hand being drawn and then replaced when a new round is started.
The idea is you start with five cards, you select which cards to keep and then the others are switched out when you click "deal", you will then be shown your new cards and told what you've won, after that you will be asked to start a new round, when you click "new round" the deck that was used previously should be discarded, a new hand taken from that deck and then drawn onto the screen.
The first two things work, the problem is that when I click "new round" it very quickly draws the hand to the screen and then replaces it with another hand, this doesn't effect the players bet, the money they have, absolutely nothing, it took me a while to notice that it was actually happening.
I can't post a working example, that would require the entire game to be uploaded (my code isn't very elegant) but I will try to show the relevant text.
Main:
int main(int argc, char *argv[])
{
srand(time(NULL));
//load static cards
SDL_Surface* deal_card = load_surface("resources/images/cards/misc/deal.png");
SDL_Surface* round_card = load_surface("resources/images/cards/misc/new_round.png");
SDL_Surface* held = load_surface("resources/images/cards/effect/held.png");
//initiate standard sdl modules
if(!init())
{
printf("fail init");
}
//initiate SDL_ttf
else if(TTF_Init() == -1)
{
printf("TTF INit fail");
}
else
{
//should exit
bool quit = false;
//events
SDL_Event e;
//font and font colour to be used for rendering text1
TTF_Font* font = TTF_OpenFont("resources/fonts/OpenSans-Regular.ttf", 18);
SDL_Color text_colour = {236, 251, 100};
//create a new deck, draw out a hand, sort it numerically, setup images and positions for cards
vector<card>my_deck = new_shuffled_deck();
vector<card>my_hand = hand(my_deck);
sort_hand(my_hand);
setup_hand(my_hand);
//should switch cards that are not held and remove those used
//must be TRUE on start otherwise the first deal will duplicate cards
bool switch_hand = true;
int round_number = 1;
//get or set bet information
read_bet(player_pot, cash_borrowed);
while(!quit)
{
//starting mouse position
int mouse_x_pos = 0;
int mouse_y_pos = 0;
//push current mouse position to starting mouse positions
SDL_GetMouseState(&mouse_x_pos, &mouse_y_pos);
//set up to blit hold icon
update_hold_position(my_hand);
//check for winning hand
winning_hand hand_details = my_scores.card_check(my_hand, bet_amount);
//setup render and blit text
render_and_blit_text(font, hand_details, player_pot, cash_borrowed, text_colour);
scoring_text(font, hand_details, text_colour);
//switch out cards that are not held
if(switch_hand == true)
{
swap_cards(my_hand, my_deck);
}
switch_hand = false;
while(SDL_PollEvent(&e) != 0)
{
if(e.type == SDL_QUIT)
{
quit = true;
}
if(e.type == SDL_MOUSEBUTTONDOWN)
{
//set mouse position to carry over without resetting
int n_mouse_pos_x = mouse_x_pos;
int n_mouse_pos_y = mouse_y_pos;
//check if card is clicked, if is selected de-select, if not selected then select
for(size_t cpc = 0; cpc < my_hand.size(); cpc++)
{
// if mouse position is in range of left side of card and right side of card
if(n_mouse_pos_x > my_hand[cpc].position.x and n_mouse_pos_x < my_hand[cpc].position.x + my_hand[cpc].image->w &&
n_mouse_pos_y > my_hand[cpc].position.y and n_mouse_pos_y < my_hand[cpc].position.y + my_hand[cpc].image->h)
{
//if clicked un-click, if un-clickde click
if(my_hand[cpc].selected == 0)
{
my_hand[cpc].selected = 1;
}
else if(my_hand[cpc].selected == 1)
{
my_hand[cpc].selected = 0;
}
}
}
//if deal is clicked
if(n_mouse_pos_x > deal_rect.x and n_mouse_pos_x < deal_rect.x + deal_card->w &&
n_mouse_pos_y > deal_rect.y and n_mouse_pos_y < deal_rect.y + deal_card->h)
{
//switch held cards, if last round switch entire hand, update cash
deal_clicked(switch_hand, round_number, my_hand, my_deck, cash_borrowed, player_pot, amount_won,
bet_amount, hand_details);
}
}
}
//blit section
//blit cards to screen
blit_cards(my_hand, round_number, held, screen_surface, deal_rect, round_card, deal_card);
SDL_Surface* fill_screen;
fill_screen = SDL_CreateRGBSurface(0, screen_width, screen_height, 32, 0, 0, 0, 0);
SDL_UpdateWindowSurface(window);
SDL_FillRect(screen_surface, 0, SDL_MapRGB(fill_screen->format, 18, 17, 233));
SDL_FreeSurface(fill_screen);
SDL_Delay(30);
}
}
close();
return 0;
}
Swap cards:
void swap_cards(vector<card>&my_hand, vector<card>&my_deck)
{
for(size_t b = 0; b < my_hand.size(); b++)
{
if(my_hand[b].selected == false)
{
SDL_FreeSurface(my_hand[b].image);
//replace card with card of the same index from the deck
my_hand[b] = my_deck[b];
// remove card from deck so it cannot be chosen again
my_deck.erase(my_deck.begin() + b);
}
else
{
// this prevents memory leak on held cards, no idea why.
SDL_FreeSurface(my_hand[b].image);
}
}
//set up images and position for cards again
setup_hand(my_hand);
}
Deal clicked:
void deal_clicked(bool &switch_hand, int &round_number, vector<card>&my_hand, vector<card>&my_deck,
int &cash_borrowed, int &player_pot, int &amount_won, int& bet_amount, winning_hand &hand_details)
{
switch_hand = true;
round_number++;
// aka if(round_number % 2 == 0 and round_number != 0)
if(round_number == 3)
{
//free card surface images
for(size_t d = 0; d < my_hand.size(); d++)
{
SDL_FreeSurface(my_hand[d].image);
}
vector<card>().swap(my_deck);
//replace deck with new deck
my_deck = new_shuffled_deck();
//draw new hand
vector<card>().swap(my_hand);
my_hand = hand(my_deck);
//sort hand by card number
sort_hand(my_hand);
//load images and position cards
setup_hand(my_hand);
//set round number back to beginning
round_number = 1;
//distribute winnings and take next bet amount
amount_won = hand_details.hand_score;
if(cash_borrowed > 0)
{
cash_borrowed -= amount_won;
}
else if(cash_borrowed < 0)
{
player_pot += abs(cash_borrowed);
cash_borrowed = 0;
}
else
{
player_pot += amount_won;
}
if(player_pot <= 0)
{
cash_borrowed +=5;
}
else
{
player_pot -= bet_amount;
}
write_bet(player_pot, cash_borrowed);
}
}
Hopefully that should be enough for someone to have an idea about where my problem is coming from.
If anyone wants more code I can post it, it jut gets even more messy, these are the only areas I think could be causing the problem, then again I can't figure out what it is.
EDIT:
Solved, duplicate call to the setup_hand function.
Duplicate call to setup_hand function.