Related
I am currently doing the exercices in Bjarne Stroustup "Programming Principles and Practice Using C++" and I finished the 8th chapter, where he talks about headers and namespaces. Using this knowledge and other online resources, I tired restructuring the Simple Calculator code into multiple header and source files in order to use the calculator function "outside" that file. I am getting 230+ errors and I really don't know why. It's quite a long file, I would highly appreciate anyone that uses their time to look at this. I will provide bellow all code snippets ( quite long )
The project's structure
NOTE: std_lib_facilites.h is provided by the book and contains only simple declarations and functions that eases the understanding of concepts.
calculator.h
#pragma once
void calculate(Token_stream& ts, Symbol_table& st);
double statement(Token_stream& ts, Symbol_table& st);
double declaration(Token_stream& ts, Symbol_table& st);
double square_root(Token_stream& ts, Symbol_table& sts);
double powerf(Token_stream& ts, Symbol_table& st);
double expression(Token_stream& ts, Symbol_table& st);
double term(Token_stream& ts, Symbol_table& st);
double factorial(Token_stream& ts, Symbol_table& st);
double primary(Token_stream& ts, Symbol_table& st);
double variable(Token_stream& ts, Symbol_table& st);
void intro_message();
void cleanup(Token_stream&);
constants.h:
#pragma once
namespace Constants
{
//Constant declarations and initializations-------------------------------------
const char number = '8'; //representation of a number type for a Token
const char sroot = 'S';
const char let = 'L'; //represents the "let" term in declaration()
const char name = 'a'; //name token
const char power = 'P';
const char vconst = 'C';
const string decl_key = "let"; //declaration keyword
const string sroot_key = "sqrt"; //keyword for calling sqare_root() function
const string power_key = "pow"; //keyword for calling power() function
const string constant_key = "const";
const char quit_key = '#';
const char print_key = ';';
const char help_key = '$';
const char show_vars = '&';
const string prompt = ">>";
const string result = "="; //used to indicate that what follows is a result
const char recover = '~'; //used as an argument for the keep_window_open() functions in catch statements
}
token.h:
#pragma once
class Token {
public:
char type;
double value;
string name; // used for a Token of type name
Token(char ch) :type{ ch }, value{ 0 } {};
Token(char ch, double val) :type{ ch }, value{ val } {};
Token(char ch, string n) :type{ ch }, name{ n } {};
};
class Token_stream {
public:
Token_stream();
Token get();
void putback(Token t);
void ignore(char c);
private:
bool isFull = false;
Token buffer;
};
variable.h:
#pragma once
class Variable
{
public:
string name;
double value;
bool isConst;
Variable(string st, double v, bool b) : name{ st }, value{ v }, isConst{ b } {}
Variable(string st, double v) : name{ st }, value{ v }, isConst{ false } {}
};
class Symbol_table
{
public:
double get_value(string s);
void set_value(string s, double n);
bool is_declared(string var);
double define_variable(string var, double val, bool isConst);
void show_variables();
private:
vector <Variable> var_table;
};
calculator.cpp:
#include "calculator.h"
#include "token.h"
#include "variable.h"
#include "constants.h"
#include "std_lib_facilities.h"
//Grammar implementation---------------------------------------------------------
using namespace Constants;
void calculate(Token_stream& ts, Symbol_table& st)
{
//double val;
while (cin)
try {
cout << prompt;
Token t = ts.get();
while (t.type == print_key) t = ts.get(); // "eat" print_key characters
if (t.type == quit_key) return; //NOTE : In void functions, you can do an empty return.
if (t.type == help_key) intro_message();
if (t.type == show_vars) st.show_variables();
else {
ts.putback(t);
cout << result << statement(ts,st) << "\n\n";
}
//ts.putback(t);
//cout << result << statement() << "\n\n";
//val = statement();
}
catch (exception& e)
{
cout.clear();
cerr << "error: " << e.what() << "\n";
cerr << "Enter " << recover << " to continue.\n";
cleanup(ts);
}
catch (...)
{
cerr << "Unknown error.\n";
cerr << "Enter " << recover << " to continue.\n";
}
}
double statement(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
switch (t.type)
{
case let:
return declaration(ts, st);
default:
ts.putback(t);
return expression(ts,st);
}
}
double declaration(Token_stream& ts, Symbol_table& st)
{
// assume we already saw "let" (in statement())
// handle: name = expression
// declare a variable called "name" with initial value "expression"
Token t = ts.get();
bool isConst = false;
if (t.type == vconst)
{
t = ts.get();
isConst = true;
if (t.type != name) error("name expected in declaration");
string var_name = t.name;
}
else if (t.type != name) error("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get();
if (t2.type != '=') error("= missing in declaration of ", var_name);
double d = expression(ts,st);
st.define_variable(var_name, d, isConst);
return d;
}
double square_root(Token_stream& ts, Symbol_table& st)
{
// get a token, assuming that we've already used the string "sqrt" in get()
Token t = ts.get();
if (t.type != '(') error("sqrt: '(' expected");
double e = expression(ts,st);
if (e < 0) error("sqrt: cannot calculate square root of negative number");
t = ts.get();
if (t.type != ')') error("sqrt: ')' expected");
return sqrt(e);
}
double powerf(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
if (t.type != '(') error("power: '(' expected");
double t1 = expression(ts,st);
t = ts.get();
if (t.type != ',') error("power: arguments must be separated by a ','");
double t2 = expression(ts, st);
if (t2 < 0) error("power: negative exponent");
t = ts.get();
if (t.type != ')') error("power: ')' expected");
return pow(t1, t2);
}
double expression(Token_stream& ts, Symbol_table& st)
{
double left = term(ts, st);
Token t = ts.get();
while (true)
{
switch (t.type)
{
case '+':
left += term(ts, st);
t = ts.get();
break;
case '-':
left -= term(ts, st);
t = ts.get();
break;
default:
ts.putback(t);
return left; // if there's no more + and -, return the result
}
}
return left;
}
double term(Token_stream& ts, Symbol_table& st)
{
double left = factorial(ts, st);
Token t = ts.get();
while (true)
{
switch (t.type)
{
//ou de paste :)
case '*':
left *= factorial(ts, st);
t = ts.get();
break;
case '/':
{
double d = factorial(ts, st);
if (d == 0) error("term: division: cannot divide by 0");
left /= d;
t = ts.get();
break;
}
case '%': //Only works for integers
{
int i1 = narrow_cast<int>(left);
int i2 = narrow_cast<int>(factorial(ts, st));
if (i2 == 0) error("term: modulo: cannot divide by 0");
left = i1 % i2;
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
return left;
}
double factorial(Token_stream& ts, Symbol_table& st)
{
double left = primary(ts, st);
Token t = ts.get();
switch (t.type)
{
case '!':
{
int lcopy = narrow_cast<int>(left);
if (left == 0) return 1; // 0! = 1
if (left < 0) error("factorial: cannot calculate factorial of a negative number");
while (lcopy > 1)
{
--lcopy;
left *= lcopy;
}
t = ts.get();
if (t.type == '!') error("factorial: unexpected '!' operator");
}
default:
ts.putback(t);
return left;
}
return left;
}
double primary(Token_stream& ts, Symbol_table& st)
{
Token t = ts.get();
switch (t.type)
{
case '(':
{
double e = expression(ts, st);
t = ts.get();
if (t.type != ')') error("primary: ')' expected");
return e;
}
case '{':
{
double e = expression(ts, st);
Token b = ts.get();
if (b.type != '}') error("primary: '}' expected");
return e;
}
case '-':
return -primary(ts, st);
case '+':
return primary(ts, st);
case number:
return t.value;
case name:
ts.putback(t);
return variable(ts, st);
case power:
return powerf(ts, st);
case sroot:
return square_root(ts, st);
default:
error("primary expexted");
}
}
double variable(Token_stream& ts, Symbol_table& st) {
Token t = ts.get();
switch (t.type)
{
case name:
{
Token t2 = t;
t = ts.get();
// check to see if it's an assignment or just a usage of the variable
if (t.type == '=')
{
double e = expression(ts, st);
st.set_value(t2.name, e);
return e;
}
else
{
ts.putback(t);
return st.get_value(t2.name);
}
}
}
}
//-------------------------------------------------------------------------------
//Additional functions-----------------------------------------------------------
void intro_message() //print a custom "banner"
{
cout << "---------------------------------------\n"
<< "|Simple calculator - V1.0 |\n"
<< "| by BIBAN|\n"
<< "---------------------------------------\n\n"
<< "Supported operators : +, -, *, /, % (for ints only), (), !-factorial\n"
<< "Supported functions :\n"
<< " - sqrt(expression) - calculates square root of any expression\n"
<< " - pow(base, exponent) - calculate a base at the power of exponent\n"
<< " --> base and exponent are expressions\n\n"
<< "Variables can be defined and used as expressions:\n"
<< " - let variable_name = value - define a variable\n"
<< " - let const constant_name = value - define a constant\n"
<< " - variable_name = new_value - assign a new value to a non-constant variable\n"
<< " - " << show_vars << " - display all variables\n\n"
<< "Use " << quit_key << " to quit the program, " << print_key << " to end an ecuation and " << help_key << " to display this message.\n"
<< "If an error occurs, type in " << recover << " to continue.\n\n";
}
void cleanup(Token_stream& ts)
{ //recover from an error
ts.ignore(recover);
}
//-------------------------------------------------------------------------------
token.cpp:
#include "token.h"
#include "constants.h"
#include "std_lib_facilities.h"
using namespace Constants;
Token_stream() :isFull(false), buffer(0) {}
Token Token_stream::get()
{
if (isFull)
{
isFull = false;
return buffer;
}
else
{
char ch;
cin >> ch;
switch (ch)
{
case '+':
case '-':
case '!':
case '*':
case '/':
case '%':
case '{':
case '}':
case '(':
case ')':
case '=': //for Variable declaration and assignment
case ',': //used for separating arguments in functions
case quit_key:
case print_key:
case help_key:
case show_vars:
return Token(ch);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
cin.putback(ch);
double d;
cin >> d;
return Token(number, d);
default:
//test if the next token is a string and return a Token if it's valid
if (isalpha(ch)) // is ch a letter ?
{
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_')) s += ch;
cin.putback(ch);
if (s == decl_key) return Token{ let };
if (s == sroot_key) return Token{ sroot };
if (s == power_key) return Token{ power };
if (s == constant_key) return Token{ vconst };
return Token{ name,s }; //Token of type name (for Variable) and value s(the name for the Variable)
}
runtime_error("Bad token.");
}
}
}
void Token_stream::putback(Token t)
{
if (isFull) runtime_error("buffer already full");
isFull = true;
buffer = t;
}
//Used to recover from errors by ignoring all characters, except char c
void Token_stream::ignore(char c) // c represents the type for the Token
{
// look in buffer
if (isFull && c == buffer.type)
{
isFull = false;
return;
}
isFull = false;
//search input
char ch = 0;
while (cin >> ch) if (ch == c) return;
}
variable.cpp:
#include "std_lib_facilities.h"
#include "variable.h"
double Symbol_table::get_value(string s)
{
// return the value of the Variable named s
for (const Variable& v : var_table)
if (v.name == s) return v.value;
error("get: undefined variable ", s);
}
void Symbol_table::set_value(string s, double n)
{
// set the variable named s to n
for (Variable& v : var_table)
{
if (v.name == s && v.isConst == false)
{
v.value = n;
return;
}
else if (v.name == s && v.isConst) error("set_value: cannot change value of a constant variable");
}
error("set: undefined variable ", s);
}
bool Symbol_table::is_declared(string var)
{
//is var in var_table already?
for (const Variable& v : var_table)
if (v.name == var) return true;
return false;
}
double Symbol_table::define_variable(string var, double val, bool isConst)
{
// add {var,val,isConst} to var_table
if (is_declared(var)) error(var, " declared twice.");
var_table.push_back(Variable{ var,val,isConst });
return val;
}
void Symbol_table::show_variables()
{
for (int i = 0; i < var_table.size(); ++i)
cout << var_table[i].name << " = " << var_table[i].value << "\n";
}
and the main.cpp file, has only a main() function:
#include "calculator.h"
#include "token.h"
#include "variable.h"
Token_stream ts;
Symbol_table st;
int main()
{
calculate(ts, st);
return 0;
}
The problem is that at the point where calculator.h is included the compiler doesn't yet know what Token_stream and Symbol_table are. This is an error, the compiler does not look forward in the code to find out what those symbols are, it just emits an error.
Three possible solutions in order from worst to best (IMHO)
Just make sure that you #include "calculator.h" after token.h and variable.h. That way when the compiler gets to calculator.h it knows what Token_stream and Symbol_table are so no error.
Add #include "token.h" and #include "variable.h" to the beginning of calculator.h. That way the compiler is forced to read token.h and variable.h before it reads the rest of calculator.h
Add forward declarations to calculator.h.
Add the code
// forward declarations
class Token_stream;
class Symbol_table;
to the beginning of calculator.h. This tells the compiler that those symbols are the names of classes. And (in this case) that's enough for the compilation to proceed.
Also, to anyone that has linker errors, mostly in Linux, MAKE TRIPLE SURE THAT YOU INCLUDE ALL THE RELATED .cpp FILES IN THE G++ COMMAND
My goal is to convert infix expression to postfix expression.
I've got a problem with this line:
while(precedence(stack[top])>=precedence(symbol))
Condition is (and should be) satisfied when it is ++ or ** or / or -- because it checks from left, but in case of power(^) it begins from the right. I don't want to enter this loop if it is doubled power(^^).
in simple in while(precedence(stack[top])>=precedence(symbol)) if the operator in top is (^)and operator in symbol is (^)I do not want to enter while loop I will remove this case because it is wrong, but I didn't know how.
C++:
#include<iostream>
#include<stdio.h>
using namespace std;#
define size 100
int temp, length = 0, inx = 0, pos = 0, top = -1;
char symbol, infix[size], postfix[size], stack[size];
void push(char);
char pop();
int precedence(char);
void infix_to_postfix(char[]);
void push(char symbol) {
if (top >= size - 1) {
cout << "stack is over flow push not possible" << endl;
} else {
top = top + 1;
stack[top] = symbol;
}
}
char pop() {
temp = stack[top];
top = top - 1;
return temp;
}
int precedence(char symbol) {
int priority = 0;
switch (symbol) {
case '+':
case '-':
priority = 1;
break;
case '*':
case '/':
priority = 2;
break;
case '^':
priority = 3;
break;
} //end of switch()
return priority;
} //end of precedence()
void infix_to_postfix(char infix[]) {
while (infix[inx] != '\0') {
symbol = infix[inx++];
switch (symbol) {
case '(':
push(symbol);
break;
case ')':
temp = pop();
while (temp != '(') {
postfix[pos++] = temp;
temp = pop();
}
break;
case '-':
case '+':
case '*':
case '/':
case '^':
while (precedence(stack[top]) >= precedence(symbol)) {
temp = pop();
postfix[pos++] = temp;
}
push(symbol);
break;
default:
postfix[pos++] = symbol;
break;
}
}
while (top > -1) {
temp = pop();
postfix[pos++] = temp;
postfix[pos] = '\0';
}
}
int main() {
cout << "\nEnter an infix expression:\n";
cin >> infix;
infix_to_postfix(infix);
cout << "\nThe equivalent postfix expression:\n";;
cout << postfix << endl;;
return 0;
}
in simple in while(precedence(stack[top])>=precedence(symbol)) if the operator in top is (^)and operator in symbol is (^)I do not want to enter while loop
Use
while (precedence(stack[top]) >= precedence(symbol) && stack[top] != '^' && symbol != '^')
Adding an && to your condition will make it check that both the operator at the top of the stack and the symbol isn't '^'.
You could also do
// ...
case '^':
if (stack[top] != '^' && symbol != '^')
while(precedence(stack[top])>=precedence(symbol))
{
temp=pop();
postfix[pos++]=temp;
}
// ...
I have started my program to test my avatar's movement in an arena. I attempted to move my avatar in any direction and my program stopped working. Using the debugger, I found a message within my Robot::move() function saying
Unhandled exception thrown: read access violation.
this was nullptr. occurred
I copied the following code in hopes that one of you can explain what I must do:
// zion.cpp
// Portions you are to complete are marked with a TODO: comment.
// We've provided some incorrect return statements (so indicated) just
// to allow this skeleton program to compile and run, albeit incorrectly.
// The first thing you probably want to do is implement the trivial
// functions (marked TRIVIAL). Then get Arena::display going. That gives
// you more flexibility in the order you tackle the rest of the functionality.
// As you finish implementing each TODO: item, remove its TODO: comment.
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
///////////////////////////////////////////////////////////////////////////
// Manifest constants
///////////////////////////////////////////////////////////////////////////
const int MAXROWS = 20; // max number of rows in the arena
const int MAXCOLS = 40; // max number of columns in the arena
const int MAXROBOTS = 130; // max number of robots allowed
const int MAXSHOTLEN = 5; // max number of steps you can shoot
const int UP = 0;
const int DOWN = 1;
const int LEFT = 2;
const int RIGHT = 3;
///////////////////////////////////////////////////////////////////////////
// Auxiliary function declarations
///////////////////////////////////////////////////////////////////////////
int decodeDirection(char dir);
void clearScreen();
///////////////////////////////////////////////////////////////////////////
// Type definitions
///////////////////////////////////////////////////////////////////////////
class Arena; // This is needed to let the compiler know that Arena is a
// type name, since it's mentioned in the Robot declaration.
class Robot
{
public:
// Constructor
Robot(Arena* ap, int r, int c);
// Accessors
int row() const;
int col() const;
// Mutators
void move();
bool takeDamageAndLive();
private:
Arena* m_arena;
int m_row;
int m_col;
// TODO: You'll probably find that a robot object needs an additional
// data member to support your implementation of the behavior affected
// by being hit and taking damage.
};
class Player
{
public:
// Constructor
Player(Arena *ap, int r, int c);
// Accessors
int row() const;
int col() const;
int age() const;
bool isDead() const;
// Mutators
string takeComputerChosenTurn();
void stand();
void move(int dir);
bool shoot(int dir);
void setDead();
private:
Arena* m_arena;
int m_row;
int m_col;
int m_age;
bool m_dead;
};
class Arena
{
public:
// Constructor/destructor
Arena(int nRows, int nCols);
~Arena();
// Accessors
int rows() const;
int cols() const;
Player* player() const;
int robotCount() const;
int nRobotsAt(int r, int c) const;
void display(string msg) const;
// Mutators
bool addRobot(int r, int c);
bool addPlayer(int r, int c);
void damageRobotAt(int r, int c);
bool moveRobots();
private:
int m_rows;
int m_cols;
Player* m_player;
Robot* m_robots[MAXROBOTS];
int m_nRobots;
};
class Game
{
public:
// Constructor/destructor
Game(int rows, int cols, int nRobots);
~Game();
// Mutators
void play();
private:
Arena* m_arena;
};
///////////////////////////////////////////////////////////////////////////
// Robot implementation
///////////////////////////////////////////////////////////////////////////
Robot::Robot(Arena* ap, int r, int c)
{
if (ap == nullptr)
{
cout << "***** A robot must be in some Arena!" << endl;
exit(1);
}
if (r < 1 || r > ap->rows() || c < 1 || c > ap->cols())
{
cout << "***** Robot created with invalid coordinates (" << r << ","
<< c << ")!" << endl;
exit(1);
}
m_arena = ap;
m_row = r;
m_col = c;
}
int Robot::row() const
{
return m_row;
}
int Robot::col() const
{
return m_col; // returns what column the robot is at.
}
void Robot::move()
{
// Attempt to move in a random direction; if we can't move, don't move
switch (rand() % 4)
{
case UP:
if (m_row > 1)
m_row--; /* Robot may move to the row above unless it is a
boundry. */
break;
case DOWN:
if (m_row < m_arena->rows())
m_row++; /* Robot may move to the row below unless it is a
boundry. */
break;
case LEFT:
if (m_col > 1)
m_col--; /* Robot may move to the left column unless it is a
boundry. */
break;
case RIGHT:
if (m_row < m_arena->cols())
m_col++; /* Robot may move to the right column unless it is a
boundry. */
break;
}
}
bool Robot::takeDamageAndLive()
{
// TODO: If the robot has been hit once before, return false (since a
// second hit kills a robot). Otherwise, return true (since the robot
// survived the damage).
return false; //temp
}
///////////////////////////////////////////////////////////////////////////
// Player implementations
///////////////////////////////////////////////////////////////////////////
Player::Player(Arena* ap, int r, int c)
{
if (ap == nullptr)
{
cout << "***** The player must be in some Arena!" << endl;
exit(1);
}
if (r < 1 || r > ap->rows() || c < 1 || c > ap->cols())
{
cout << "**** Player created with invalid coordinates (" << r
<< "," << c << ")!" << endl;
exit(1);
}
m_arena = ap;
m_row = r;
m_col = c;
m_age = 0;
m_dead = false;
}
int Player::row() const
{
return m_row;
}
int Player::col() const
{
return m_col;
}
int Player::age() const
{
return m_age;
}
string Player::takeComputerChosenTurn()
{
// Your replacement implementation should do something intelligent
// and return a string that describes what happened. When you've
// decided what action to take, take it by calling move, shoot, or stand.
// This function must return one of the following four strings:
// "Moved."
// "Shot and hit!"
// "Shot and missed!"
// "Stood."
// Here's one possible strategy:
// If moving in some direction would put me in less immediate danger
// than standing, then move in that direction.
// else shoot in the direction of the nearest robot I can hit.
// A more aggressive strategy is possible, where you hunt down robots.
int r = row(), c = col();
int nRobotsLeft = 0, nRobotsRight = 0, nRobotsUp = 0, nRobotsDown = 0;
if (c > 1) // checks the number of robots 1 space to the left of the player.
nRobotsLeft = m_arena->nRobotsAt(r, c - 1);
if (c < m_arena->cols()) /* checks the number of robots 1 space to the right
of the player. */
nRobotsRight = m_arena->nRobotsAt(r, c + 1);
if (r > 1) // checks the number of robots 1 space above the player
nRobotsUp = m_arena->nRobotsAt(r - 1, c);
if (r < m_arena->rows()) /* checks the number of robots 1 space under the
player. */
nRobotsDown = m_arena->nRobotsAt(r + 1, c);
int nRobotsLeftRow = 0, nRobotsRightRow = 0, nRobotsAboveCol = 0,
nRobotsBelowCol = 0;
for (int q = c - 2; q >= 1; q--) /* looking for robots on the same row to
the left, but not adjacent to the player */
nRobotsLeftRow += m_arena->nRobotsAt(r, q); /* increments the number of
robots if any are found */
for (int q = c + 2; q <= m_arena->cols(); q++) /* looking for robots on
the same row, to the right, but not adjacent to the player */
nRobotsRightRow += m_arena->nRobotsAt(r, q); /* increments the number of
robots if any are found */
for (int k = r - 2; k >= 1; k--) /* looks for robots in the same column,
above but not adjacent to the player */
nRobotsAboveCol += m_arena->nRobotsAt(k, c);
for (int k = r + 2; k <= m_arena->rows(); k++) /* looks for robots in the
same column, below but not adjacent to the player */
nRobotsBelowCol += m_arena->nRobotsAt(k, c);
// # robots directly next to player
int nRobotsAdjacent = nRobotsRight + nRobotsLeft + nRobotsUp + nRobotsDown;
int nRobotsInRange = nRobotsRightRow + nRobotsLeftRow + nRobotsAboveCol +
nRobotsBelowCol; //# robots not directly next to player but in shooting range
if (nRobotsAdjacent == 0) /* if no robots are adjacent to the player, */ {
if (nRobotsInRange > 0) /* and there are robots in shooting range, the player
should shoot towards a robot. */
{
if (nRobotsRightRow > 0) /*shoots right*/ {
bool hit = shoot(RIGHT);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
}
else if (nRobotsLeftRow > 0) /*shoots left*/ {
bool hit = shoot(LEFT);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
}
else if (nRobotsAboveCol > 0) /*shoots up*/ {
bool hit = shoot(UP);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
}
else if (nRobotsBelowCol > 0) /*shoots down*/ {
bool hit = shoot(DOWN);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
}
else /* this should never trigger. only goes off if an error occurs. */ {
stand();
return "Stood.";
}
}
else /* player is safe, but has nothing to shoot at */ {
stand();
return "Stood.";
}
}
if (nRobotsAdjacent > 0) {
/* should calculate the safest of the 4 spots and choose a spot to move to based on relative safety */
int safetyRatingR = m_arena->nRobotsAt(r + 1, c + 1) + m_arena->nRobotsAt(r - 1, c + 1) + m_arena->nRobotsAt(r, c + 2);
// creates an integer value of the amount of robots directly adjacent to the space 1 spot to the right of the player
int safetyRatingL = m_arena->nRobotsAt(r + 1, c - 1) + m_arena->nRobotsAt(r - 1, c - 1) + m_arena->nRobotsAt(r, c - 2);
// safety value for the left space
int safetyRatingU = m_arena->nRobotsAt(r - 1, c - 1) + m_arena->nRobotsAt(r - 1, c + 1) + m_arena->nRobotsAt(r - 2, c);
//safety value for the above space
int safetyRatingD = m_arena->nRobotsAt(r + 1, c + 1) + m_arena->nRobotsAt(r + 1, c - 1) + m_arena->nRobotsAt(r + 2, c);
//safety value for the below space
if (safetyRatingR <= safetyRatingL && safetyRatingR <= safetyRatingU && safetyRatingR <= safetyRatingD && c != m_arena->cols()) {
// if R has the lowest safety rating, and moving right would not take you off the grid
move(RIGHT);
return "Moved.";
}
else if ((safetyRatingL <= safetyRatingU) && (safetyRatingL <= safetyRatingD) && (c != 1)) {
// if L has the lowest safety rating of the remaining possible directions to move, and moving left would not go off the grid
move(LEFT);
return "Moved.";
}
else if ((safetyRatingU <= safetyRatingD) && (r != 1)) {
// if U has the lowest safety rating of the 2 remaining poss directions, and moving up would not go off the grid
move(UP);
return "Moved.";
}
else if (r != m_arena->rows()) { // final option is to move down, only triggers if this would not take you off the grid
move(DOWN);
return "Moved.";
}
else { // something went wrong, and none of the 4 directions will work
stand();
return "Stood.";
}
}
}
void Player::stand()
{
m_age++;
}
void Player::move(int dir)
{
m_age++;
switch (dir)
{
case UP:
if (m_row > 1)
m_row--;
break;
case DOWN:
if (m_row < m_arena->rows())
m_row++;
break;
case LEFT:
if (m_col > 1)
m_col--;
break;
case RIGHT:
if (m_col < m_arena->cols())
m_col++;
break;
}
}
bool Player::shoot(int dir)
{
m_age++;
if (rand() % 3 == 0) // miss with 1/3 probability
return false;
// Damages the nearest robot in direction dir, returning
// true if a robot is hit and damaged, false if not hit.
switch (dir) {
case UP:
for (int r = m_row; r >= 1; r--) /* starts at the player's row position
and steps down, stopping at the end of the arena */
{
if (m_arena->nRobotsAt(r, m_col) >= 1) /* if there is a robot at the
current step, throw damage and return */
{
m_arena->damageRobotAt(r, m_col);
return true;
}
}
break;
case DOWN:
for (int r = m_row; r <= m_arena->rows(); r++) /* starts at player's row
and goes down */
{
if (m_arena->nRobotsAt(r, m_col) >= 1) /* if there is a robot at the
current step, throw damage and return */
{
m_arena->damageRobotAt(r, m_col);
return true;
}
}
break;
case LEFT:
for (int c = m_col; c >= 1; c--) /* starts at player's column position
and steps to the left one space at a time */
{
if (m_arena->nRobotsAt(m_row, c) >= 1) {
m_arena->damageRobotAt(m_row, c); /* if there is a robot at the
current step, throw damage and return */
return true;
}
}
break;
case RIGHT:
for (int c = m_col; c <= m_arena->cols(); c++) /* starts at player's column
position and steps to the right one space at a time */
{
if (m_arena->nRobotsAt(m_row, c) >= 1) {
m_arena->damageRobotAt(m_row, c); /* if there is a robot at the
current step, throw damage and return */
return true;
}
}
break;
}
return false; // didn't hit anything
}
bool Player::isDead() const
{
// returns whether the player is dead.
if (m_dead)
return true;
return false;
}
void Player::setDead()
{
m_dead = true;
}
///////////////////////////////////////////////////////////////////////////
// Arena implementations
///////////////////////////////////////////////////////////////////////////
Arena::Arena(int nRows, int nCols)
{
if (nRows <= 0 || nCols <= 0 || nRows > MAXROWS || nCols > MAXCOLS)
{
cout << "***** Arena created with invalid size " << nRows << " by "
<< nCols << "!" << endl;
exit(1);
}
m_rows = nRows;
m_cols = nCols;
m_player = nullptr;
m_nRobots = 0;
}
Arena::~Arena()
{
for (int i = 0; i < m_nRobots; i++) {
delete m_robots[i]; /* deletes all remaining dynamically allocated
robots */
m_nRobots--;
}
delete m_player; // deletes the player
}
int Arena::rows() const
{
// TODO: TRIVIAL: Return the number of rows in the arena.
// Delete the following line and replace it with the correct code.
return m_rows; // This implementation compiles, but is incorrect.
}
int Arena::cols() const
{
// TODO: TRIVIAL: Return the number of columns in the arena.
// Delete the following line and replace it with the correct code.
return m_cols; // This implementation compiles, but is incorrect.
}
Player* Arena::player() const
{
return m_player;
}
int Arena::robotCount() const
{
return m_nRobots;
}
int Arena::nRobotsAt(int r, int c) const
{
// returns the number of robots at row r, column c.
int tally = 0;
for (int j = 0; j < m_nRobots; j++)
if (m_robots[j]->row() == r && m_robots[j]->col() == c)
tally++; /* adds a robot to the tally if it is positioned at
(r, c) */
return tally;
}
void Arena::display(string msg) const
{
// Position (row,col) in the arena coordinate system is represented in
// the array element grid[row-1][col-1]
char grid[MAXROWS][MAXCOLS];
int r, c;
// Fill the grid with dots
for (r = 0; r < rows(); r++)
for (c = 0; c < cols(); c++)
grid[r][c] = '.';
// Indicate each robot's position
/*for (r = 1; r <= rows(); r++) {
for (c = 1; c <= cols(); c++) {
if (nRobotsAt(r, c) == 1)
grid[r][c] = 'R'; /* If one robot is at some grid point,
char is set to 'R'. */
/*else if (nRobotsAt(r, c) > 1 && nRobotsAt(r, c) < 9)
grid[r][c] = nRobotsAt(r, c); /* If it's 2 though 8, char
is set to '2' through '8'.
else if (nRobotsAt(r, c) >= 9)
grid[r][c] = '9'; // For 9 or more, char is set to '9'.
else
grid[r][c] = '.';
}
}*/
// Indicate player's position
if (m_player != nullptr)
{
// Set the char to '#', unless there's also a robot there,
// in which case set it to '*'.
char& gridChar = grid[m_player->row() - 1][m_player->col() - 1];
if (gridChar == '.')
gridChar = '#';
else
gridChar = '*';
}
// Draw the grid
clearScreen();
for (r = 0; r < rows(); r++)
{
for (c = 0; c < cols(); c++)
cout << grid[r][c];
cout << endl;
}
cout << endl;
// Write message, robot, and player info
cout << endl;
if (msg != "")
cout << msg << endl;
cout << "There are " << robotCount() << " robots remaining." << endl;
if (m_player == nullptr)
cout << "There is no player." << endl;
else
{
if (m_player->age() > 0)
cout << "The player has lasted " << m_player->age() << " steps." << endl;
if (m_player->isDead())
cout << "The player is dead." << endl;
}
}
bool Arena::addRobot(int r, int c)
{
// If the maximum amount of robots exists, none shall be added.
if (m_nRobots = MAXROBOTS)
return false;
// Dynamically allocates another robot and add it to arena
m_robots[m_nRobots] = new Robot(this, r, c);
m_nRobots++;
return true;
}
bool Arena::addPlayer(int r, int c)
{
// Don't add a player if one already exists
if (m_player != nullptr)
return false;
// Dynamically allocate a new Player and add it to the arena
m_player = new Player(this, r, c);
return true;
}
void Arena::damageRobotAt(int r, int c)
{
// TODO: Damage one robot at row r, column c if at least one is there.
// If the robot does not survive the damage, destroy it.
if (nRobotsAt(r, c) > 0) {
for (int l = 0; l < m_nRobots; l++) {
/* the following statement looks through the robot array to
find an element that has coordinates matching the robot taking
damage */
if (m_robots[l]->row() == r && m_robots[l]->col() == c) {
// TODO: robot takes damage
delete m_robots[l]; // removes this element
while (l < m_nRobots) {
m_robots[l] = m_robots[l + 1]; /* copies all elements
over one element */
l++;
}
m_nRobots--; // decrements the number of all robots
}
}
}
}
bool Arena::moveRobots()
{
for (int k = 0; k < m_nRobots; k++)
{
m_robots[k]->move(); // robot makes a move
/* If that move results in that robot being in the same
position as the player, the player dies. */
if (m_robots[k]->row() == m_player->row() && m_robots[k]->col() == m_player->col())
m_player->setDead();
}
// return true if the player is still alive, false otherwise
return !m_player->isDead();
}
///////////////////////////////////////////////////////////////////////////
// Game implementations
///////////////////////////////////////////////////////////////////////////
Game::Game(int rows, int cols, int nRobots)
{
if (nRobots > MAXROBOTS)
{
cout << "***** Trying to create Game with " << nRobots
<< " robots; only " << MAXROBOTS << " are allowed!" << endl;
exit(1);
}
// Create arena
m_arena = new Arena(rows, cols);
// Add player
int rPlayer = 1 + rand() % rows;
int cPlayer = 1 + rand() % cols;
m_arena->addPlayer(rPlayer, cPlayer);
// Populate with robots
while (nRobots > 0)
{
int r = 1 + rand() % rows;
int c = 1 + rand() % cols;
// Don't put a robot where the player is
if (r == rPlayer && c == cPlayer)
continue;
m_arena->addRobot(r, c);
nRobots--;
}
}
Game::~Game()
{
delete m_arena;
}
void Game::play()
{
Player* p = m_arena->player();
if (p == nullptr)
{
m_arena->display("");
return;
}
string msg = "";
do
{
m_arena->display(msg);
msg = "";
cout << endl;
cout << "Move (u/d/l/r/su/sd/sl/sr/c//q): ";
string action;
getline(cin, action);
if (action.size() == 0)
p->stand();
else
{
switch (action[0])
{
default: // if bad move, nobody moves
cout << '\a' << endl; // beep
continue;
case 'q':
return;
case 'c': // computer moves player
msg = p->takeComputerChosenTurn();
break;
case 'u':
case 'd':
case 'l':
case 'r':
p->move(decodeDirection(action[0]));
break;
case 's':
if (action.size() < 2) // if no direction, nobody moves
{
cout << '\a' << endl; // beep
continue;
}
switch (action[1])
{
default: // if bad direction, nobody moves
cout << '\a' << endl; // beep
continue;
case 'u':
case 'd':
case 'l':
case 'r':
if (p->shoot(decodeDirection(action[1])))
msg = "Hit!";
else
msg = "Missed!";
break;
}
break;
}
}
m_arena->moveRobots();
} while (!m_arena->player()->isDead() && m_arena->robotCount() > 0);
m_arena->display(msg);
}
///////////////////////////////////////////////////////////////////////////
// Auxiliary function implementations
///////////////////////////////////////////////////////////////////////////
int decodeDirection(char dir)
{
switch (dir)
{
case 'u': return UP;
case 'd': return DOWN;
case 'l': return LEFT;
case 'r': return RIGHT;
}
return -1; // bad argument passed in!
}
///////////////////////////////////////////////////////////////////////////
// main()
///////////////////////////////////////////////////////////////////////////
int main()
{
// Initialize the random number generator. (You don't need to
// understand how this works.)
srand(static_cast<unsigned int>(time(0)));
// Create a game
// Use this instead to create a mini-game: Game g(3, 3, 2);
Game g(15, 18, 80);
// Play the game
g.play();
}
///////////////////////////////////////////////////////////////////////////
// clearScreen implementations
///////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY OR REMOVE ANY CODE BETWEEN HERE AND THE END OF THE FILE!!!
// THE CODE IS SUITABLE FOR VISUAL C++, XCODE, AND g++ UNDER LINUX.
// Note to Xcode users: clearScreen() will just write a newline instead
// of clearing the window if you launch your program from within Xcode.
// That's acceptable.
#ifdef _MSC_VER // Microsoft Visual C++
#include <windows.h>
void clearScreen()
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi);
DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
COORD upperLeft = { 0, 0 };
DWORD dwCharsWritten;
FillConsoleOutputCharacter(hConsole, TCHAR(' '), dwConSize, upperLeft,
&dwCharsWritten);
SetConsoleCursorPosition(hConsole, upperLeft);
}
#else // not Microsoft Visual C++, so assume UNIX interface
#include <cstring>
void clearScreen() // will just write a newline in an Xcode output window
{
static const char* term = getenv("TERM");
if (term == nullptr || strcmp(term, "dumb") == 0)
cout << endl;
else
{
static const char* ESC_SEQ = "\x1B["; // ANSI Terminal esc seq: ESC [
cout << ESC_SEQ << "2J" << ESC_SEQ << "H" << flush;
}
}
#endif
/*main.cpp
Open with*/
Your code assumes that arrays are indexed from 1 to N (for example, r < 1 || r > ap->rows()), but in C and C++, arrays go from 0 to N-1. Accessing an array at position N is an access violation.
In addRobot, you have an assignment in your if statement, and not a comparison. This results in not adding any robots, and eventually dereferencing a NULL pointer.
Also, you leak memory in Arena::~Arena, since you change the value of m_nRobots in the loop which results in some Robots not being deleted.
I have a struct that looks like this:
// A lexical scope that keeps track of every declared variable
// in a function
struct LexState
{
// A map containing the index of a variable in it's function
// The key is the variable name
std::unordered_map<std::string, int> _vars;
Function *_function;
LexState *_parent;
};
This is the code using the struct:
#define DD(msg) std::cout << "Debug: " << msg << '\n'
#define DD1(msg, p1) std::cout << "Debug: " << msg << p1 << '\n'
#define DD2(msg, p1, p2) std::cout << "Debug: " << msg << p1 << p2 << '\n'
// A lexical scope that keeps track of every declared variable
// in a function
struct LexState
{
// A map containing the index of a variable in it's function
// The key is the variable name
std::unordered_map<std::string, int> _vars;
Function *_function;
LexState *_parent;
LexState(LexState *parent, Function *function)
{
_parent = parent;
_function = function;
}
Value *SearchVar(char *name)
{
if (_parent == nullptr)
{
return nullptr;
}
std::string str(name);
auto var = _parent->_vars.find(str);
if (var == _parent->_vars.end())
{
return _parent->SearchVar(name);
}
return _function->GetLocalVar(var->second)->GetValue();
}
};
class Compiler
{
public:
State *_state;
Function *_current_function;
GC *_gc;
LexState *_ls;
Compiler(State *state) :
_state(state)
{
_current_function = nullptr;
_gc = state->GetGC();
_current_function = new Function(nullptr);
_state->_main_function = _current_function;
_ls = nullptr;
}
void PushFunction()
{
Function *new_function = new Function(_current_function);
_current_function = new_function;
LexState *new_ls = new LexState(_ls, new_function);
// even now, right after creating new_ls, new_ls->_vars is 0
printf("%p\n", &new_ls->_vars);
_ls = new_ls;
}
void PopFunction()
{
_current_function = _current_function->_parent;
LexState *parent = _ls->_parent;
delete _ls;
_ls = parent;
}
int DeclareVar(const Symbol *node)
{
assert(node->_type == NODE_SYMBOL);
DD("Declaring variable");
auto token = node->_token;
char name[token->len + 1];
memcpy(name, token->pos, token->len);
name[token->len] = '\0';
int idx = _current_function->AddLocalVar(name);
std::string key(name);
printf("%p\n", &_ls->_vars);
if (_ls != nullptr)
_ls->_vars.insert(std::make_pair(key, idx));
else
DD("LexState nullptr");
DD("Variable declared");
return idx;
}
void Compile(const Node *node)
{
switch (node->_type)
{
case NODE_CHUNK:
CompileChunk((Chunk *)node);
break;
case NODE_BLOCK:
CompileBlock((Block *)node);
break;
case NODE_FUNCTION_DEF:
CompileFunctionDef((FunctionDef *)node);
break;
case NODE_CONDITIONAL:
CompileConditional((ConditionalStatement *)node);
break;
case NODE_BINARY_EXPR:
CompileBinaryExpr((BinaryExpr *)node);
break;
case NODE_UNARY_EXPR:
CompileUnaryExpr((UnaryExpr *)node);
break;
case NODE_SYMBOL:
CompileSymbol((Symbol *)node);
break;
case NODE_STRING_LITERAL:
CompileStringLiteral((StringLiteral *)node);
break;
case NODE_BOOL_LITERAL:
CompileBoolLiteral((BoolLiteral *)node);
break;
case NODE_INT_LITERAL:
CompileIntLiteral((IntLiteral *)node);
break;
}
}
void CompileChunk(const Chunk *chunk)
{
Compile(chunk->_block);
AddCode(OP_HALT);
}
void CompileBlock(const Block *block)
{
std::vector<Node *> vec = block->_vec;
for (auto it = vec.begin(); it != vec.end(); it++)
{
Compile(*it);
}
}
void CompileFunctionDef(const FunctionDef *func)
{
Value v;
int f = AddConstant(v);
AddCode(OP_PUSH, f);
Value *vp = _current_function->GetConstant(f);
if (func->_name)
{
int fvar = DeclareVar((Symbol *)func->_name);
AddCode(OP_STOR_LOCAL, fvar);
}
ArgList *argsnode = (ArgList *)func->_args;
auto args = argsnode->_vec;
int argcount = args.size();
PushFunction();
auto closure = new Closure(_current_function);
closure->_argcount = argcount;
std::cout << argcount << '\n';
vp->_closure = closure;
vp->_type = VALUE_CLOSURE;
// Compiling inside function
// Arguments are compiled in reserved order, because in the function call
// the passed arguments will be pushed in the order they are passed
for (auto it = args.rbegin(); it != args.rend(); it++)
{
int var = DeclareVar((Symbol *)*it);
AddCode(OP_STOR_LOCAL, var);
}
if (func->_guard)
{
Compile(func->_guard);
}
else
{
Compile(func->_body);
}
AddCode(OP_RETURN);
// End function
PopFunction();
}
void CompileConditional(const ConditionalStatement *node)
{
auto function = _current_function;
Compile(node->_condition);
int cond_res_idx = function->AddLocalVar();
AddCode(OP_DUP);
// Store the condition result into an internal variable
AddCode(OP_STOR_LOCAL, cond_res_idx);
AddCode(OP_DUP);
int true_jmp = AddCode(OP_JMP_T, 0);
int false_jmp = AddCode(OP_JMP_F, 0);
// Save index of the first code of the block
int block_idx = function->_code.size();
// Jump to block when condition is true
function->ChangeCode(true_jmp, block_idx);
Compile(node->_expr1);
AddCode(OP_PUSH_LOCAL, cond_res_idx);
// Jump to the end of the whole if-elif-else statement
// if the condition result was true
int exit_jmp = AddCode(OP_JMP_T, 0);
// Save index of the first code after this statement
int right_idx = function->_code.size();
function->ChangeCode(false_jmp, right_idx);
if (node->_expr2 != nullptr)
{
Compile(node->_expr2);
}
else
{
AddCode(OP_PUSH_NIL);
}
int end_idx = function->_code.size();
function->ChangeCode(exit_jmp, end_idx);
}
void CompileBinaryExpr(const BinaryExpr *expr)
{
auto function = _current_function;
auto token = expr->_op->type;
if (token == TOKEN_ASSIGN)
{
if (expr->_left->_type == NODE_SYMBOL)
{
int var = DeclareVar((Symbol *)expr->_left);
Compile(expr->_right);
AddCode(OP_STOR_LOCAL, var);
return;
}
}
// A function call
if (token == TOKEN_LEFTPAREN)
{
ArgList *arglist = (ArgList *)expr->_right;
auto args = arglist->_vec;
int argcount = args.size();
// A function call cannot have more than 255 arguments
assert(argcount < 256);
for (auto it = args.begin(), end = args.end();
it != end; it++)
{
Compile(*it);
}
Compile(expr->_left);
AddCode(OP_CALL, argcount);
return;
}
Compile(expr->_left);
// Both 'and' and 'or' expressions does short circuit
if (token == TOKEN_BOOL_AND ||
token == TOKEN_BOOL_OR)
{
AddCode(OP_DUP);
OpType op = (token == TOKEN_BOOL_AND) ? OP_JMP_F : OP_JMP_T;
int idx = AddCode(op, function->_ip + 1);
Compile(expr->_right);
int next = function->_code.size();
uint32_t instr = function->_code[idx];
function->ChangeCode(idx, next);
return;
}
// No need for lazy evaluation, compile normally
Compile(expr->_right);
switch (expr->_op->type)
{
case TOKEN_ADD:
AddCode(OP_ADD);
break;
case TOKEN_SUB:
AddCode(OP_SUB);
break;
case TOKEN_MUL:
AddCode(OP_MUL);
break;
case TOKEN_DIV:
AddCode(OP_DIV);
break;
case TOKEN_POW:
AddCode(OP_POW);
break;
case TOKEN_AND:
AddCode(OP_AND);
break;
case TOKEN_OR:
AddCode(OP_OR);
break;
case TOKEN_XOR:
AddCode(OP_XOR);
break;
case TOKEN_LT:
AddCode(OP_LT);
break;
case TOKEN_GT:
AddCode(OP_GT);
break;
case TOKEN_LTEQ:
AddCode(OP_LTEQ);
break;
case TOKEN_GTEQ:
AddCode(OP_GTEQ);
break;
case TOKEN_SHIFT_L:
AddCode(OP_SHIFT_L);
break;
case TOKEN_SHIFT_R:
AddCode(OP_SHIFT_R);
break;
}
}
void CompileUnaryExpr(const UnaryExpr *expr)
{
Compile(expr->_right);
switch (expr->_token->type)
{
case TOKEN_SUB:
AddCode(OP_NEGATE);
break;
case TOKEN_NOT:
AddCode(OP_NOT);
break;
case TOKEN_BOOL_NOT:
AddCode(OP_BOOL_NOT);
break;
}
}
// This function gets called only when it's a reference
void CompileSymbol(const Symbol *node)
{
auto token = node->_token;
char name[token->len + 1];
memcpy(name, token->pos, token->len);
name[token->len] = '\0';
DD1("Searching reference: ", name);
Value *upvalue = _ls->SearchVar(name);
if (upvalue)
{
int idx = _current_function->AddUpValue(upvalue);
AddCode(OP_PUSH_UPVALUE, idx);
return;
}
int idx = _current_function->GetLocalVarIndex(name);
AddCode(OP_PUSH_LOCAL, idx);
}
void CompileStringLiteral(const StringLiteral *sl)
{
Value v(sl->_token->str, sl->_token->len);
AddCode(OP_PUSH, AddConstant(v));
}
void CompileBoolLiteral(const BoolLiteral *bl)
{
Value v(bl->_token->type == TOKEN_TRUE);
AddCode(OP_PUSH, AddConstant(v));
}
void CompileIntLiteral(const IntLiteral *il)
{
Value v(il->_token->num);
AddCode(OP_PUSH, AddConstant(v));
}
int AddCode(OpType code)
{
return _current_function->AddCode(code);
}
int AddCode(OpType code, int a)
{
return _current_function->AddCode(code, a);
}
int AddConstant(const Value &v)
{
return _current_function->AddConstant(v);
}
};
The program outputs:
Declaring variable
0
It crashes before the DD("Variable declared") part.
Even though i'm new to C++, I'm pretty sure I don't need to allocate the unordered map myself, right? As it's not a pointer, it will be allocated when I do new LexState
So is there any initialization/allocation I am forgetting?
%i is not the right format specifier to use to print a pointer. You are most likely getting the output that represents a truncated value of the pointer. Use %p instead.
printf("%p\n", &state->_vars);
I don't see anything wrong using state->_vars. See working code at http://ideone.com/YAJK5K.
first post here.
I'm doing a university assignment where I'm required to finish implementing a program that simulates the opening bid of a game of contract bridge. I was provided with the following file that contains the main function:
/// File: bridge.cpp
/// Creates a deck of cards, shuffles them and displays them.
#include <iostream>
#include <iomanip>
#include <fstream>
#include "game.h"
const int NUM_DEALS = 4;
using namespace std;
int main(int argc, char *argv[]) {
Game game;
ifstream infile;
bool fromFile = false;
if (argc == 2) {
// open the file and check it exists
infile.open(argv[1]);
if (infile.fail()) {
cerr << "Error: Could not find file" << endl;
return 1;
}
fromFile = true;
}
for (int deal = 0; deal < NUM_DEALS; deal++) {
game.setup(fromFile);
if (fromFile) {
infile >> game;
}
game.deal();
game.auction();
cout << game << endl;
cout << endl << "==============================================================" << endl << endl;
game.nextDealer();
}
// close the file
if (argc == 2) {
infile.close();
}
return 0;
}
I've completed the other classes that simulate the card objects, the deck object, the hand objects and the game object but my program crashed when I clicked "Build and Run". The next logical step was to debug, but when I did so I got the following output in the debugger window:
Building to ensure sources are up-to-date
Selecting target:
Release
Adding source dir: C:\Users\Jack\Documents\CodeBlocks\BridgeAssignment\
Adding source dir: C:\Users\Jack\Documents\CodeBlocks\BridgeAssignment\
Adding file: C:\Users\Jack\Documents\CodeBlocks\BridgeAssignment\bin\Release\BridgeAssignment.exe
Changing directory to: C:/Users/Jack/Documents/CodeBlocks/BridgeAssignment/.
Set variable: PATH=.;C:\Program Files (x86)\CodeBlocks\MinGW\bin;C:\Program Files (x86)\CodeBlocks\MinGW;C:\Program Files (x86)\PHP;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Intel\iCLS Client;C:\Program Files\Intel\iCLS Client;C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem;C:\Windows\System32\WindowsPowerShell\v1.0;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Sony\VAIO Improvement;C:\Program Files (x86)\Sony\VAIO Startup Setting Tool;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files (x86)\Jahshaka\..\gtk2\bin;C:\Program Files (x86)\Jahshaka\..\mlt\bin;C:\Program Files (x86)\OpenLibraries\bin;C:\Users\Jack\AppData\Local\Smartbar\Application;C:\Program Files\Microsoft\Web Platform Installer;C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0;C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit;C:\Program Files\Microsoft SQL Server\110\Tools\Binn;C:\Program Files (x86)\QuickTime\QTSystem;C:\Program Files (x86)\Autodesk\Backburner
Starting debugger: C:\Program Files (x86)\CodeBlocks\MINGW\bin\gdb.exe -nx -fullname -quiet -args C:/Users/Jack/Documents/CodeBlocks/BridgeAssignment/bin/Release/BridgeAssignment.exe
done
Registered new type: wxString
Registered new type: STL String
Registered new type: STL Vector
Setting breakpoints
Reading symbols from C:\Users\Jack\Documents\CodeBlocks\BridgeAssignment\bin\Release\BridgeAssignment.exe...(no debugging symbols found)...done.
Debugger name and version: GNU gdb (GDB) 7.5
Child process PID: 13628
Program received signal SIGSEGV, Segmentation fault.
In ?? () ()
If it would help in solving the problem, here are my other classes.
Card.h:
#include <string>
#include <iostream>
#ifndef CARD_H
#define CARD_H
using namespace std;
enum Suit {CLUBS, DIAMONDS, HEARTS, SPADES};
enum Rank {TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE};
class Card
{
public:
Card();
Card(Rank rank, Suit suit);
Card(string);
~Card();
Rank getRank();
Suit getSuit();
bool operator() (Card*, Card*);
friend ostream& operator<<(ostream&, Card&);
private:
Suit suit;
Rank rank;
};
#endif // CARD_H
Card.cpp:
#include "card.h"
#include <iostream>
Card::Card()
{
this->suit = CLUBS;
this->rank = TWO;
}
Card::Card(Rank rank, Suit suit)
{
this->suit = suit;
this->rank = rank;
}
Card::Card(string str)
{
char rank = str.at(0);
char suit = str.at(1);
Rank cardRank = TWO;
Suit cardSuit = CLUBS;
switch(rank)
{
case '2': cardRank = TWO;
break;
case '3': cardRank = THREE;
break;
case '4': cardRank = FOUR;
break;
case '5': cardRank = FIVE;
break;
case '6': cardRank = SIX;
break;
case '7': cardRank = SEVEN;
break;
case '8': cardRank = EIGHT;
break;
case '9': cardRank = NINE;
break;
case 'T': cardRank = TEN;
break;
case 'J': cardRank = JACK;
break;
case 'Q': cardRank = QUEEN;
break;
case 'K': cardRank = KING;
break;
case 'A': cardRank = ACE;
break;
}
switch(suit)
{
case 'C': cardSuit = CLUBS;
break;
case 'D': cardSuit = DIAMONDS;
break;
case 'H': cardSuit = HEARTS;
break;
case 'S': cardSuit = SPADES;
break;
}
this->suit = cardSuit;
this->rank = cardRank;
}
Card::~Card()
{
}
Rank Card::getRank()
{
return this->rank;
}
Suit Card::getSuit()
{
return this->suit;
}
bool Card::operator() (Card* cardOne, Card* cardTwo)
{
if (cardOne->getSuit() > cardTwo->getSuit())
{
return cardOne->getRank() >= cardTwo->getRank();
}
else if (cardOne->getSuit() < cardTwo->getSuit())
{
return cardOne->getRank() > cardTwo->getRank();
}
else
{
return cardOne->getRank() > cardTwo->getRank();
}
}
ostream& operator <<(ostream& out, Card& card)
{
string cardID = "";
switch(card.getRank())
{
case TWO: cardID += "2";
break;
case THREE: cardID += "3";
break;
case FOUR: cardID += "4";
break;
case FIVE: cardID += "5";
break;
case SIX: cardID += "6";
break;
case SEVEN: cardID += "7";
break;
case EIGHT: cardID += "8";
break;
case NINE: cardID += "9";
break;
case TEN: cardID += "T";
break;
case JACK: cardID += "J";
break;
case QUEEN: cardID += "Q";
break;
case KING: cardID += "K";
break;
case ACE: cardID += "A";
}
switch(card.getSuit())
{
case CLUBS: cardID += "C";
break;
case DIAMONDS: cardID += "D";
break;
case HEARTS: cardID += "H";
break;
case SPADES: cardID += "S";
break;
}
out << cardID;
return out;
}
Deck.h:
#ifndef DECK_H
#define DECK_H
#include "card.h"
#include <iostream>
using namespace std;
class Deck
{
public:
Deck();
virtual ~Deck();
void reset();
Card* dealNextCard();
void shuffle();
friend ostream& operator<<(ostream&, Deck&);
friend istream& operator>>(istream&, Deck&);
private:
int cardsDealt;
Card** deckArray;
};
#endif // DECK_H
Deck.cpp:
#include "Deck.h"
#include "Card.h"
#include "random.h"
#include <iostream>
Deck::Deck()
{
deckArray = new Card*[52];
int counter = 0;
cardsDealt = 0;
for (int i = 0; i < 13; i++)
{
for (int j = 0; j < 4; j++)
{
*deckArray[counter] = Card((Rank)i, (Suit)j);
counter++;
}
}
}
Deck::~Deck()
{
//dtor
}
void Deck::reset()
{
cardsDealt = 0;
}
Card* Deck::dealNextCard()
{
cardsDealt++;
return deckArray[cardsDealt-1];
}
void Deck::shuffle()
{
Random rand;
int index1 = rand.randomInteger(0,53);
int index2 = rand.randomInteger(0,53);
Card temp = *deckArray[index1];
deckArray[index1] = deckArray[index2];
*deckArray[index2] = temp;
}
ostream& operator<<(ostream& out, Deck& deck)
{
string cards = "";
for (int i = 0; i < 52; i++)
{
out << " " << deck.deckArray[i];
}
return out;
}
istream& operator>>(istream& in, Deck& deck)
{
string text[52];
for (int i = 0; i < 52; i++)
{
in >> text[i];
}
for (int i = 0; i < 52; i++)
{
char rank = text[i].at(0);
char suit = text[i].at(1);
Rank cardRank = TWO;
Suit cardSuit = CLUBS;
switch(rank)
{
case '2': cardRank = TWO;
break;
case '3': cardRank = THREE;
break;
case '4': cardRank = FOUR;
break;
case '5': cardRank = FIVE;
break;
case '6': cardRank = SIX;
break;
case '7': cardRank = SEVEN;
break;
case '8': cardRank = EIGHT;
break;
case '9': cardRank = NINE;
break;
case 'T': cardRank = TEN;
break;
case 'J': cardRank = JACK;
break;
case 'Q': cardRank = QUEEN;
break;
case 'K': cardRank = KING;
break;
case 'A': cardRank = ACE;
break;
}
switch(suit)
{
case 'C': cardSuit = CLUBS;
break;
case 'D': cardSuit = DIAMONDS;
break;
case 'H': cardSuit = HEARTS;
break;
case 'S': cardSuit = SPADES;
break;
}
*deck.deckArray[i] = Card(cardRank, cardSuit);
}
return in;
}
Hand.h:
#ifndef HAND_H
#define HAND_H
#include "card.h"
#include <iostream>
#include <vector>
#include <set>
class Hand
{
public:
Hand();
virtual ~Hand();
void clear();
void addCard(Card*);
string makeBid();
friend ostream& operator<< (ostream&, Hand&);
private:
unsigned int strength;
unsigned int highCardPoints;
unsigned int lengthPoints;
int addHighCards(vector<Card*>);
int findLengthPoints(vector<Card*>);
void getStrength();
bool balanced;
void getBalance();
void getWinningSuit();
set<int> suitLen;
vector<Card*> clubs;
vector<Card*> diamonds;
vector<Card*> hearts;
vector<Card*> spades;
vector< vector<Card*> > winningSuit;
vector<string> winningSuitStr;
};
#endif // HAND_H
Hand.cpp:
#include "Hand.h"
#include "card.h"
#include <vector>
#include <set>
#include <iterator>
#include <iostream>
Hand::Hand()
{
strength = 0;
balanced = false;
}
Hand::~Hand()
{
//dtor
}
void Hand::clear()
{
clubs.clear();
diamonds.clear();
hearts.clear();
spades.clear();
strength = 0;
balanced = false;
}
void Hand::addCard(Card* card)
{
switch(card->getSuit())
{
case CLUBS: clubs.push_back(card);
break;
case DIAMONDS: diamonds.push_back(card);
break;
case HEARTS: hearts.push_back(card);
break;
case SPADES: spades.push_back(card);
break;
}
}
void Hand::getBalance()
{
if ((suitLen.count(4)==2 && suitLen.count(3)==1 && suitLen.count(2)==1)
|| (suitLen.count(4)==1 && suitLen.count(3)==3))
{
balanced = true;
}
else
{
balanced = false;
}
}
void Hand::getWinningSuit()
{
if (clubs.size() >= diamonds.size() && clubs.size() >= hearts.size() && clubs.size() >= spades.size())
{
winningSuit.push_back(clubs);
winningSuitStr.push_back("C");
}
if (diamonds.size() >= clubs.size() && diamonds.size() >= hearts.size() && diamonds.size() >= spades.size())
{
winningSuit.push_back(diamonds);
winningSuitStr.push_back("D");
}
if (hearts.size() >= clubs.size() && hearts.size() >= diamonds.size() && hearts.size() >= spades.size())
{
winningSuit.push_back(hearts);
winningSuitStr.push_back("H");
}
if (spades.size() >= clubs.size() && spades.size() >= diamonds.size() && spades.size() >= hearts.size())
{
winningSuit.push_back(spades);
winningSuitStr.push_back("S");
}
}
int Hand::addHighCards(vector<Card*> suit)
{
int highCardPoints = 0;
for (unsigned int i = 0; i < suit.size(); i++)
{
switch(suit[i]->getRank())
{
case ACE: highCardPoints += 4;
break;
case KING: highCardPoints += 3;
break;
case QUEEN: highCardPoints += 2;
break;
case JACK: highCardPoints += 1;
break;
default:
break;
}
}
return highCardPoints;
}
int Hand::findLengthPoints(vector<Card*> suit)
{
if (suit.size() > 4)
{
return suit.size() - 4;
}
return 0;
}
void Hand::getStrength()
{
highCardPoints = 0;
lengthPoints = 0;
highCardPoints += addHighCards(clubs);
highCardPoints += addHighCards(diamonds);
highCardPoints += addHighCards(hearts);
highCardPoints += addHighCards(spades);
lengthPoints += findLengthPoints(clubs);
lengthPoints += findLengthPoints(diamonds);
lengthPoints += findLengthPoints(hearts);
lengthPoints += findLengthPoints(spades);
strength = highCardPoints + lengthPoints;
}
string Hand::makeBid()
{
suitLen.insert(clubs.size());
suitLen.insert(diamonds.size());
suitLen.insert(hearts.size());
suitLen.insert(spades.size());
getStrength();
getBalance();
getWinningSuit();
if (balanced)
{
if (strength >= 0 && strength <= 12)
{
if (suitLen.count(6)==1)
{
if (winningSuit[0]==clubs)
{
return "PASS";
}
else
{
return "2" + winningSuitStr[0];
}
}
else if (suitLen.count(6)==2)
{
return "2" + winningSuitStr[0];
}
else if (suitLen.count(7)==1)
{
return "3" + winningSuitStr[0];
}
else if (suitLen.count(8)==1)
{
return "4" + winningSuitStr[0];
}
else
{
return "PASS";
}
}
else if (strength >= 13 && strength <= 21)
{
if (winningSuit.size()==2 && winningSuit[0].size()>=5)
{
return "1" + winningSuitStr[0];
}
else if (winningSuit.size()>=2 && winningSuit[0].size()==4)
{
return "1" + winningSuitStr[winningSuitStr.size()-1];
}
else
{
return "1" + winningSuitStr[0];
}
}
else /**< if (strength >= 22)*/
{
return "2C";
}
}
else
{
if (strength >= 0 && strength <= 12)
{
return "PASS";
}
else if (strength == 13 || strength == 14 || strength == 18 || strength == 19)
{
if (clubs.size() == diamonds.size())
{
if (clubs.size() == 4)
{
return "1D";
}
return "1C";
}
else
{
if (clubs.size() > diamonds.size())
{
return "1C";
}
return "1D";
}
}
else if (strength >= 15 && strength <= 17)
{
return "1 NT";
}
else if (strength == 20 || strength == 21)
{
return "2 NT";
}
else
{
return "2C";
}
}
}
ostream& operator<<(ostream& out, Hand& hand)
{
out << "SPADES : ";
for (unsigned int i = 0; i < hand.spades.size(); i++)
{
out << hand.spades[i] << " ";
}
out << endl;
out << "HEARTS : ";
for (unsigned int i = 0; i < hand.hearts.size(); i++)
{
out << hand.hearts[i] << " ";
}
out << endl;
out << "DIAMONDS : ";
for (unsigned int i = 0; i < hand.diamonds.size(); i++)
{
out << hand.diamonds[i] << " ";
}
out << endl;
out << "CLUBS : ";
for (unsigned int i = 0; i < hand.clubs.size(); i++)
{
out << hand.clubs[i] << " ";
}
out << endl;
out << hand.highCardPoints << " HCP, " << hand.lengthPoints << " LP, Total = " << hand.strength << endl;
return out;
}
Game.h:
#ifndef GAME_H
#define GAME_H
#include <iostream>
#include "Deck.h"
#include "Hand.h"
enum Position {NORTH, EAST, SOUTH, WEST};
class Game
{
public:
Game();
virtual ~Game();
void setup(bool);
void deal();
void auction();
void nextDealer();
friend ostream& operator<< (ostream&, Game&);
friend istream& operator>> (istream&, Game&);
private:
Deck gameDeck;
Hand** gameHands;
Position dealer;
string* openingBid;
};
#endif // GAME_H
Game.cpp:
#include "Game.h"
#include "Deck.h"
#include "Hand.h"
#include <iostream>
Game::Game()
{
gameDeck = Deck();
gameHands = new Hand*[4];
gameHands[0] = new Hand();
gameHands[1] = new Hand();
gameHands[2] = new Hand();
gameHands[3] = new Hand();
dealer = NORTH;
}
Game::~Game()
{
//dtor
}
void Game::setup(bool fromFile)
{
if (!fromFile)
{
gameDeck.shuffle();
gameDeck.reset();
for (unsigned int i = 0; i < 4; i++)
{
gameHands[i]->clear();
}
}
}
void Game::deal()
{
for (unsigned int i = 0; i < 52; i++)
{
gameHands[(i%4)+1]->addCard(gameDeck.dealNextCard());
}
}
void Game::auction()
{
openingBid = new string[2];
openingBid[0] = "PASS";
openingBid[1] = "PASS";
for (unsigned int i = 0; i < 4; i++)
{
if (gameHands[i]->makeBid() != "PASS")
{
switch(i)
{
case 0: openingBid[0] = "NORTH";
break;
case 1: openingBid[0] = "EAST";
break;
case 2: openingBid[0] = "SOUTH";
break;
case 3: openingBid[0] = "WEST";
break;
}
openingBid[1] = gameHands[i]->makeBid();
break;
}
}
}
void Game::nextDealer()
{
int temp = (int)dealer;
temp++;
temp = temp % 4;
dealer = (Position)temp;
}
ostream& operator<< (ostream& out, Game& game)
{
out << "NORTH";
out << game.gameHands[0];
out << endl;
out << "EAST";
out << game.gameHands[1];
out << endl;
out << "SOUTH";
out << game.gameHands[2];
out << endl;
out << "WEST";
out << game.gameHands[3];
out << endl;
return out;
}
istream& operator>> (istream& in, Game& game)
{
in >> game.gameDeck;
return in;
}
Also here is the premade random class I'm using.
Random.h:
#ifndef _random_h
#define _random_h
/// This class provides several functions for generating pseud-random numbers.
///
class Random {
public:
/// \brief
///
/// Initialize the randomizer.
///
Random();
/// \brief
///
/// Generates a random integer number greater than or equal to low and less than high.
/// \param low int - lower bound for range (inclusive).
/// \param high int - upper bound for range (exclusive).
/// \return int - A random integer number greater than or equal to low and less than high.
///
int randomInteger(int low, int high);
/// \brief
/// Generates a random real number greater than or equal to low and less than high.
///
/// \param low double - lower bound for range (inclusive).
/// \param high double - upper bound for range (exclusive).
/// \return double - A random real number greater than or equal to low and less than high.
///
double randomReal(double low, double high);
/// \brief
/// Generates a true false outcome based on the probability p.
/// Calling randomChance(0.30) returns true 30% of the time.
///
/// \param p double - Value between 0 (never) and 1 (always).
/// \return bool - true or false based on p.
///
bool randomChance(double p);
private:
/// \brief
///
/// Initializes teh random-number generator so that its results are unpredictable. If this function is
/// not called the other functions will return the same values on each run.
///
void randomize();
};
#endif // _random_h
Random.cpp:
#include <cstdlib>
#include <ctime>
#include "random.h"
/// This class provides several functions for generating pseud-random numbers.
///
Random::Random() {
randomize();
}
/// \brief
/// Generates a random integer number greater than or equal to low and less than or equal to high.
///
/// \param low int - lower bound for range (inclusive).
/// \param high int - upper bound for range (inclusive).
/// \return int - A random integer number greater than or equal to low and less than or equal to high.
///
int Random::randomInteger(int low, int high) {
double d = double(rand()) / (double(RAND_MAX) + 1);
int k = int(d * (high - low + 1));
return low + k;
}
/// \brief
/// Generates a random real number greater than or equal to low and less than high.
///
/// \param low double - lower bound for range (inclusive).
/// \param high double - upper bound for range (exclusive).
/// \return double - A random real number greater than or equal to low and less than high.
///
double Random::randomReal(double low, double high) {
double d = double(rand()) / (double(RAND_MAX) + 1);
return low + d * (high - low);
}
/// \brief
/// Generates a true false outcome based on the probability p.
/// Calling randomChance(0.30) returns true 30% of the time.
///
/// \param p double - Value between 0 (never) and 1 (always).
/// \return bool - true or false based on p.
///
bool Random::randomChance(double p) {
return randomReal(0, 1) < p;
}
/// \brief
///
/// Initializes the random-number generator so that its results are unpredictable. If this function is
/// not called the other functions will return the same values on each run.
///
void Random::randomize() {
srand(int(time(NULL)));
}
I've been hunting around the internet for solutions to this problem, but nothing seemed to fit my scenario, with the crash on run and the SIGSEGV on debug. Hoping someone here can help me, I'm getting desperate lol.
Thanks.
Your code is way too much to be sure but the problem resides very likely in yout deck constructor.
Deck::Deck()
{
// Here you allocate space for 52 pointers, not Cards
deckArray = new Card*[52];
...
// at this location there's no space for a card but for a pointer
//*deckArray[counter] = Card((Rank)i, (Suit)j);
// so you should do this instead:
deckArray[counter] = new Card((Rank)i, (Suit)j);
// to allocate needed space for the card
...
}
To sum it up, you never allocate the space needed to store the cards.
The deckArray in Deck is an array of pointers, but you never allocate its elements.
This means that all your assignments through these pointers are invalid dereferences whose behaviour is undefined.
You need to either allocate those elements with new, or avoid pointers, which is usually the best way.
Replacing Hand** gameHands; with Hand gameHands[4]; and Card** deckArray; with Card deckArray[52]; would be a good start.
(Unlike in java, where "new" is the only way to instantiate classes, C++ programmers usually only use that word when necessary.)
SIGSEGV usually raises when you dereference a nullpointer or uninitialized pointer.
If you don't know how to use breakpoints, I suggest using logging macro to figure out, where the problem is. For example:
//#define NOLOGGING // uncomment this line to disable logging
#include <iostream>
#ifndef NOLOGGING
#define LOG(msg)
#else
#define LOG(msg) std::cout << "[LOG] (" __FILE__ ":" << __LINE__ << ") from " << __func__ << "()\n " << msg << "\n";
#endif
You are targeting the Release version of the project, and you don't get any debug information. Change the build target to Debug and you would get:
Building to ensure sources are up-to-date
Selecting target:
Debug
done
Registered new type: wxString
Registered new type: STL String
Registered new type: STL Vector
Setting breakpoints
Debugger name and version: GNU gdb (GDB) 7.6.1-ubuntu
Program received signal SIGSEGV, Segmentation fault.
At /home/user/src/card/bridge/Deck.cpp:15
Which would lead you to the answer dogiordano provided.
Make sure you are giving argument at load time with a run command (parallelly) in your gdb as example:
fix.c
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char** argv)
{
int sum;
sum=(atoi(argv[1])+ atoi( argv[2]));
printf("sum=%d",sum);
}
I compile with option -g
gcc -g fix.c
gdb a.out
you will be enter to gdb here you need to run your code in gdb with argument note if command line argument are missed
run enter (missing argument)
"Program received signal SIGSEGV, Segmentation fault."
so give here make sure to give argument
run 1 2
Now its good !!