The Ideal GasPump!

Alex Edwards 0 Tallied Votes 178 Views Share

Hmm this implementation is a bit bulky and unnecessary, but it was required as a project for one of my classes.


Programming perspective:

You'll notice in the code that I'm still fairly new to manipulating streams directly in C++, but I am trying my best =P.

The StandardIncremental overhead could have been omitted by simply extracting the value using the extraction operator, though I'm still new to using that so I did things the hard way (initially by accident, but I've learned my lesson! =) )

The rest is up to you other programmers to inspect =P.


User perspective:

Whoo! Have fun! =)

/**
 * GasPump.h
 * 
 * This header is for representing a real-life GasPump.
 */
#pragma once
#include "StandardIncremental.h"
#include <ctime>
#include <string>
#include <sstream>
#include <exception>

using std::size_t;
using std::string;
using std::exception;
const double Conv_Factor = .03; // $3.00 = 1 gallon... yes I know, wishful thinking @_@

string conversionFactor();

namespace D{
	enum Direction {OUT, IN};
}

using D::Direction;
using D::OUT;
using D::IN;

class GasPump{
	
	StandardIncremental money;
	StandardIncremental moneySpent;
	StandardIncremental gas;
	StandardIncremental currentGasDispensed;
	size_t capacity;
	string buff;
	void reset();

	public:
		class GasPumpException : public exception{
			public:
				GasPumpException();
				virtual const char* what() const throw();
		} gpe;

		GasPump(size_t val = 0, size_t cap = 500);
		void insertMoney(const StandardIncremental&);
		void insertGas(const StandardIncremental&);
		void pumpOnce(Direction);
		void pumpGas(Direction);
		void showTotalGas();
		void showTotalMoney();
		void showCurrentGasDispensed();
		void showCurrentMoneyUsed();
		void showSession();
		StandardIncremental dispenseChange();
};

//////////////////////////

/**
 * StandardIncremental.h
 * 
 * This header is for representing real-life StandardIncremental
 * things (i.e. Money, Volume, etc).
 */
#pragma once
#include <iostream>
#include <string>

using std::string;

class StandardIncremental{
	double value;
	void truncate(); // truncates the double, should only happen once
	void truncate() const;

	public:
		StandardIncremental(); // constructor (void)
		StandardIncremental(double); // constructor (double)
		StandardIncremental(const string&); // constructor (string)
		string getValue(); // returns the value of this money object
		StandardIncremental operator+(const StandardIncremental&);
		StandardIncremental& operator++(int);
		StandardIncremental operator-(const StandardIncremental&);
		StandardIncremental& operator--(int);
		StandardIncremental& operator+=(const StandardIncremental&);
		StandardIncremental& operator-=(const StandardIncremental&);
		StandardIncremental operator *(const StandardIncremental&);
		bool operator==(const StandardIncremental&);
		bool operator>(const StandardIncremental&);
		bool operator>=(const StandardIncremental&);
		bool operator<=(const StandardIncremental&);
		bool operator<(const StandardIncremental&);
		friend std::ostream& operator<< (std::ostream&, const StandardIncremental&);
		friend std::istream& operator>> (std::istream&, const StandardIncremental&);
		operator double ();
		operator double () const;
};

//////////////////

/**
 * GasPump.cpp
 * 
 * This is the implementation file that follows the GasPump.h interface.
 */
#include "GasPump.h"
#include <cmath>
#include <iostream>
#include <sstream>
#include <cmath>

using std::cin;
using std::cout;
using std::endl;
using std::stringstream;
using std::ios;
using D::OUT;
using D::IN;

/**
 * Constructor for the GasPump
 *
 * If the value supplied is greater than the capacity provided,
 * the value becomes the capacity.
 */
GasPump::GasPump(size_t val, size_t cap) : gas(val * 1.00), capacity(cap), money(0.00),
				 moneySpent(0.00), currentGasDispensed(0.00){
	if(val >= cap){
		gas = static_cast<double>(capacity);
	}
}

/**
 * Default Constructor for th GasPumpException inner class of GasPump
 */
GasPump::GasPumpException::GasPumpException() : exception(){}

/**
 * In the event an illegal operation occurs on the GasPump, such as attempting to supply
 * a negative amount of cash for the pump.
 */
const char* GasPump::GasPumpException::what() const throw(){
	return "An illegal operation occured on the GasPump!";
}

/**
 * Attempts to insert money into the GasPump.
 * 
 * This function will fail if the GasPump doesn't have enough gas to support
 * the amount of money inserted.
 */
void GasPump::insertMoney(const StandardIncremental& mon){
	if( const_cast<StandardIncremental&>(mon) // i.e. if $3.00 < (gas * 100 * Conv_Factor)
			<  static_cast<const StandardIncremental>( gas * 
					static_cast<const StandardIncremental>(100 * Conv_Factor) ) ){
		
		if( fabs(static_cast<double>(mon)) != static_cast<double>(mon) ) throw gpe; // no stealing allowed!

		money += mon;
	}
}

/**
 * Attempts to increase the current amount of gas available
 */
void GasPump::insertGas(const StandardIncremental& g){
	StandardIncremental cap = static_cast<double>(capacity);
	if( cap > (const_cast<StandardIncremental&>(g) + gas) 
		&& ((static_cast<double>(g) + static_cast<double>(gas)) >= 0  )){ // if the capacity is greater than
																	      // the sum of the current amount and incoming amount
																	      // and the amount is not negative
		gas += g; // increment the current amount by incoming amount
	}
}

/**
 * Increases or Decreases the current amount of gas once and only once.
 */
void GasPump::pumpOnce(Direction dir){
	if(dir == IN){
		if( ( gas + static_cast<StandardIncremental>(1.00) ) <=
					static_cast<const StandardIncremental>(capacity) ){ // if its possible to increment gas withouuuu
																		// exceeding the capacity...
			gas++; // increment the gas
		}
	}else{ // if dir == OUT
		StandardIncremental lowest = 0.03;
		bool gasCanBePumped = ( lowest <= ( gas * static_cast<const StandardIncremental&>(Conv_Factor * 100) ) ); // is there gas left 
																										          // to spend money on?
		if( (money > moneySpent) && (gasCanBePumped == true) ){ // if the amount of money inserted is greater than the amount spent...
														        // and there is gas left to be extracted
			moneySpent++; // increment the amount of money spent
			double dMod = fmod(moneySpent, Conv_Factor); // obtain the remainder of moneySpent / Conv_Factor
			stringstream ss  (stringstream::in | stringstream::out); // input/output stringstream
			ss.precision(5); // knock-off factor for single/double precision floating point types
			ss.setf(ios::fixed, ios::floatfield); // sets the format of the stringstream
			ss << dMod; // place untruncated value in streammm
			ss >> dMod; // truncate the value

			if( dMod == 0.01 ){ // if the remainder of the amount spent and the conversion
								// factor is 0, increment the currentGasDispensed and
								// decrement the current amount of gas
				// edit: fmod is pretty funky... it wont return 0.00, but instread 0.01-to-arg ??
				currentGasDispensed++;
				gas--;
			}
		}
	}
}

/**
 * Pumps gas in or out, depending on the direction specified.
 * Continuously pumps so long as the user confirms the input
 * of the char 'p'.
 */
void GasPump::pumpGas(Direction dir){
	while(true){
		buff.clear();
		std::cin >> buff;

		if(buff[0] == 'p'){
			pumpOnce(dir);
		}else return;
	}
}

/**
 * Shows the current gas dispensed during a session.
 */
void GasPump::showCurrentGasDispensed(){
	cout << " " << currentGasDispensed << "gal" << endl;
}

/**
 * Shows the money used during a session.
 */
void GasPump::showCurrentMoneyUsed(){
	cout << "$" << moneySpent << endl;
}

/**
 * Shows the money inserted during a session.
 */
void GasPump::showTotalMoney(){
	cout << "$" << money << endl;
}

/**
 * Shows the current amount of gas available
 * out of the maximum capacity.
 */
void GasPump::showTotalGas(){
	cout << gas << "gal/" << capacity << "gal" << endl;
}

/**
 *  Simply invokes both the current gas dispensed and money used.
 */
void GasPump::showSession(){
	showCurrentMoneyUsed();
	showCurrentGasDispensed();
}

/**
 * Changes the money inserted, the money spent and the current gas dispenesed to be
 * of value 0. This is used so that any new customers of the Gas Station will be able
 * to see and use their own sessions.
 */
void GasPump::reset(){
	currentGasDispensed = 0.00;
	money = 0.00;
	moneySpent = 0.00;
}

/**
 * Dispenses change and invokes reset
 */
StandardIncremental GasPump::dispenseChange(){
	StandardIncremental s = (money - moneySpent);
	reset();
	return s;
}

/**
 * Returns the string object version of the Conv_Factor
 */
string conversionFactor(){
	stringstream ss (stringstream::in | stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << Conv_Factor;
	return ss.str();
}

///////////////////////

/**
 * StandardIncremental.cpp
 * 
 * This is the implementation file for the StandardIncremental.h interface.
 */
#include "StandardIncremental.h"
#include <sstream>
#include <iostream>
#include <exception>

using std::stringstream;
using std::ios;

StandardIncremental::StandardIncremental() : value(0.0) {
	truncate();
}

void StandardIncremental::truncate(){
	stringstream ss (stringstream::in | stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << value;
	ss >> value; // push new truncated value into the double
}

void StandardIncremental::truncate() const{
	const_cast<StandardIncremental&>(*this).truncate();
}

StandardIncremental::StandardIncremental(double v = 0.0) : value(v) {
	truncate();
}

StandardIncremental::StandardIncremental(const std::string& val){
	try{
		stringstream ss (stringstream::in | stringstream::out);
		ss << val;
		ss >> value;
		truncate();
	}catch(std::exception& e){
		std::cout << "ERROR!\n" << e.what() << "\n";
		exit(1);
	}
}

string StandardIncremental::getValue(){
	stringstream ss (stringstream::out);
	ss.precision(2);
	ss.setf(ios::fixed, ios::floatfield);
	ss << value;
	return ss.str();
}


StandardIncremental StandardIncremental::operator+(const StandardIncremental& other){
	StandardIncremental temp  = *this;
	temp.value += other.value;
	return temp;
}

StandardIncremental& StandardIncremental::operator++(int){
	return ((*this) += 0.01);
}

// this will msot likely be changed
StandardIncremental StandardIncremental::operator-(const StandardIncremental& other){
	StandardIncremental temp = *this;
	temp.value -= other.value;
	return temp;
}

StandardIncremental& StandardIncremental::operator--(int){
	return ((*this) -= 0.01);
}

StandardIncremental& StandardIncremental::operator+=(const StandardIncremental& other){
	this->value += other.value;
	return *this;
}

// this will most likely be changed
StandardIncremental& StandardIncremental::operator-=(const StandardIncremental& other){
	this->value -= other.value;
	return *this;
}

StandardIncremental StandardIncremental::operator *(const StandardIncremental& other){
	StandardIncremental temp = *this;
	temp.value *= other.value;
	return temp;
}

StandardIncremental::operator double(){
	return this->value;
}

StandardIncremental::operator double() const{
	return this->value;
}

bool StandardIncremental::operator>(const StandardIncremental& other){
	return this->value > other.value;
}

bool StandardIncremental::operator<(const StandardIncremental& other){
	return this->value < other.value;
}

bool StandardIncremental::operator>=(const StandardIncremental& other){
	return this->value >= other.value;
}

bool StandardIncremental::operator<=(const StandardIncremental& other){
	return this->value <= other.value;
}

std::ostream& operator<<( std::ostream& out, const StandardIncremental& mon ){
	return out << mon.value;
}

std::istream& operator>>(std::istream& in, const StandardIncremental& mon){
	double& d = const_cast<double&>(mon.value);
	if( !(in >> d) ){
		std::cout << "ERROR!\n";
		exit(1);
	}
	mon.truncate();
	return in;
}

////////////////////////

/**
 * GasPump Project
 * Author: Alex Edwards
 * Date: 10/07/08
 *
 * Intent: The intent of this program is to simulate a typical
 * GasPump that we encounter when we have a need to fuel our automobiles,
 * or anything that requires gas.
 */

/*includes needed for this program*/
#include "GasPump.h"
#include "StandardIncremental.h"
#include <windows.h>
#include <iostream>
#include <ctime>
#include <string>
#include <sstream>

/*using directive for namespaces of objects and types commonly used*/
using std::string;
using std::size_t;
using std::clock_t;
using D::Direction;
using std::cout;
using std::cin;
using std::endl;
using std::ios;
using std::stringstream;

/*Prototypes. See method definitions under main for full descriptions*/
VOID keyboard(KEY_EVENT_RECORD&); // Handles key input-events received from the EDT
VOID fadeOut(); // Blacks out the screen
void pause(size_t); // Pauses a process
void unsetFlags(); // Initializes mask


typedef INPUT_RECORD QUEUE; // renamed for more clerity
typedef COORD POINT2D; // renamed for clerity


enum {PUMP_OUT_ONCE = 80, EXIT = 69, SEE_DESCRIPTIONS = 83, REFILL = 82, 
	  INSERT = 73, LOAD_ONCE = 76, SEE_MENU = 77, DISPENSE_CHANGE = 67, C_FACTOR = 70}; // convenience-constants
enum {SHOW_BANNER = 1, SHOW_MONEY_SPENT = 2, SHOW_MONEY = 4, SHOW_GAS_DISPENSED = 8, 
	  SHOW_GAS = 16, SHOW_MENU_DESCRIPTION = 32, SHOW_C_FACTOR = 64}; // Flags for bit-wise operations
enum {MAX_BIT = 6}; // The maximum number of bits


GasPump gp (50, 500); // Our GasPump with initial amount of 50 gallons, with a maximum capacity of 500 gallons!
POINT2D loc = {0, 0}; // Specifying a point of return once we're finished manipulating the ConsoleScreen
bool wasMenuKey = true; // might be removed
bool finishedLoop = true; // for re-use of exiting loops
unsigned char mask = 1; // mask for flags
string menuBanner (
"************************************************\n\
***        Welcome to the Gas Station!       ***\n\
***                                          ***\n\
***  'p': pump out gas |  's': menu info     ***\n\
***  'r': refill pump  |  'e': exit program  ***\n\
***  'i': insert money |  'l': load pump     ***\n\
***  'c': get change   |  'm': show menu     ***\n\
***  'f': conv factor  |                     ***\n\
************************************************\n"
); // The Menu

string menuDescription (
"-----------------------------------------------------------------\n\
|'p' - Gradually pumps gas out of the GasPump\t\t\t|\n\
|'r' - Gradually refills the GasPump \t\t\t\t|\n\
|'i' - Insert cash if you wish to purchase gas from the GasPump\t|\n\
|'c' - Obtain the money you didn't spend in a session \t\t|\n\
|'f' - Displays the conversion factor\t\t\t\t|\n\
|'s' - Displays this help menu on the screen \t\t\t|\n\
|'e' - Shuts down the program \t\t\t\t\t|\n\
|'l' - Gives the GasPump a bulk amount of gas \t\t\t|\n\
|'m' - Displays the standard menu\t\t\t\t|\n\
-----------------------------------------------------------------\n"
); // The Instructions for using the commands specified in the GasPump menu.

// OUT seems to already be defined. I don't want OUT or IN in some header to overlap with my
// constants declared in a namespace. Since im not using the 
#ifdef OUT
#undef OUT
#endif

#ifdef IN
#undef IN
#endif

HANDLE standardInputHandle; // Handle to the Standard Input
HANDLE outputHandle; // Handle to the Standard Output
DWORD pendingReq; // Amount of requests read
QUEUE requestQueue[256]; // Holds up to 256 requests
DWORD count; // Stores information for methods that accept DWORD references
CONSOLE_SCREEN_BUFFER_INFO csbi; // Contains information of the Console Screen

/**
 * Entry-point for the program.
 */
int main(){

    standardInputHandle = GetStdHandle(STD_INPUT_HANDLE); // Attempt to get handle to the Standard Input
	if (standardInputHandle == INVALID_HANDLE_VALUE){ // If unsuccessful...
		cout << "Unable to get the handle to the standard input! Closing program!\n";
		exit(1); // exit program with an irregular exit code (1)
	}

	outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); // also attempt to get handle to the Standard Output
	if (outputHandle == INVALID_HANDLE_VALUE){ // If unsuccessful...
		cout << "Unable to get the handle to the standard output! Closing program!\n";
		exit(1); // exit program with an irregular exit code (1)
	}
	
	cout.precision(2); // convenience for gas and cash display
	cout.setf( ios::fixed, ios::floatfield ); // formats the standard output to show what we want to see

    while(true){ 
		if(wasMenuKey){ // if a menu key was pressed, re-display the menu (??, most likely will be changed)
			fadeOut(); // clear the screen
			if(mask & SHOW_BANNER) // show banner mask
				cout << menuBanner << endl;
			if(mask & SHOW_MONEY_SPENT) // show money spent mask
				gp.showCurrentMoneyUsed();
			if(mask & SHOW_MONEY) // show money mask
				gp.showTotalMoney();
			if(mask & SHOW_GAS_DISPENSED) // show gas dispensed mask
				gp.showCurrentGasDispensed();
			if(mask & SHOW_GAS) // show gas mask
				gp.showTotalGas();
			if(mask & SHOW_MENU_DESCRIPTION) // show menu description mask
				cout << menuDescription << endl;
			if(mask & SHOW_C_FACTOR){ // show the conversion factor
				cout << "The conversion factor of $-to-gallons is: ";
				cout << conversionFactor() << ":0.01" << endl;
			}

			wasMenuKey = false;
		}

        if (! ReadConsoleInput( standardInputHandle, requestQueue, 
			256, &pendingReq) ){ // if the input to the console couldn't be read
			cout << "An error occurred while attempting to read input. Closing program!\n";
			exit(1); // exit the program with an irregular exit code (1)
		}
 
        for (DWORD i = 0; i < pendingReq; i++){ // For every request pushed in the Queue
            switch(requestQueue[i].EventType){ // get the Event type of the request
                case KEY_EVENT: 
                    keyboard(requestQueue[i].Event.KeyEvent); 
                    break;
                default: // Not really concerned with any other event type.
                    break; 
            }
        }
    }
    return 0; 
};

/**
 * Convenience method for takking a KEY_EVENT_RECORD and
 * performing an action based on the key pressed.
 */
VOID keyboard(KEY_EVENT_RECORD& key){
	
	if(key.bKeyDown){
		switch(key.wVirtualKeyCode){ // in the event the user presses the appropriate menu button...
			case PUMP_OUT_ONCE:
				gp.pumpOnce(D::OUT);
				unsetFlags();
				mask |= SHOW_MONEY_SPENT;
				mask |= SHOW_GAS_DISPENSED;
				pause(100);
				break;
			case EXIT:
				cout << "Thank you, come again! =)\nPress any key to continue...";
				cin.get();
				cout << "\n";
				exit(0);
				break; // probably unnecessary, since the program will end before this can happen
			case SEE_DESCRIPTIONS:
				unsetFlags();
				mask |= SHOW_MENU_DESCRIPTION;
				break;
			case REFILL:
				unsetFlags();
				cout << "Fill'er up!" << endl;
				gp.pumpOnce(D::IN);
				mask |= SHOW_GAS;
				pause(200);
				break;
			case INSERT:
				do{
					finishedLoop = true;
					fadeOut();
					unsetFlags();
					try{
						cout << "Submit cash into the machine? y/n ";
						string choice;
						cin >> choice;
						
						if(choice[0] == 'y'){
							double cash;
							cout << "Enter the amount you wish to submit.\n";
							stringstream ss (stringstream::in | stringstream::out);
							string val;
							cin >> val;
							ss << val;
							if(!(ss >> cash)) throw ss.str();
							gp.insertMoney(cash);
							cout << "You can spend up to ";
							gp.showTotalMoney();
							cout << "Submit any key to continue. ";
							cin.ignore();
							cin.get();
							mask |= SHOW_BANNER;
							mask |= SHOW_MONEY;
						}else{
							mask |= SHOW_BANNER;
						}
					}catch(string& errval){
						cout << "\a\a" << errval << ": IS AN ILLEGAL AMOUNT!" << endl;
						//cin.clear();
						finishedLoop = false;
					}catch(...){
						cout << "Thief! Shutting down program!" << endl;
						cin.ignore();
						cin.get();
						exit(1);
					}
				}while(!finishedLoop);
				break;
			case LOAD_ONCE:
				do{
					finishedLoop = true;
					fadeOut();
					unsetFlags();
					try{
						cout << "Would you like to bulk load gas into the gas pump? y/n ";
						string theChoice;
						cin >> theChoice;
						if(theChoice[0] == 'y'){
							cout << "Specify the amount of gas you would like to bulk-load" << endl;
							double gasAmount;
							stringstream ss (stringstream::in | stringstream::out);
							string val;
							cin >> val;
							ss << val;
							if(!(ss >> gasAmount)) throw ss.str();
							gp.insertGas(gasAmount);
							cout << "Submit any key to continue.";
							cin.ignore();
							cin.get();
							mask |= SHOW_BANNER;
							mask |= SHOW_GAS;
						}else{
							mask |= SHOW_BANNER;
						}
					}catch(string& illegalAmount){ // if illegal input, user has to try again
						cout << "\a\a" << illegalAmount << ": IS AN ILLEGAL AMOUNT!" << endl;
						//cin.clear();
						finishedLoop = false;
					}catch(...){ // if another exception occurred, end the program
						cout << "An error occurred! Closing program!\n";
						exit(1);
					}
				}while(!finishedLoop);
				break;
			case SEE_MENU:
				unsetFlags();
				mask |= SHOW_BANNER;
				break;
			case C_FACTOR:
				unsetFlags();
				mask |= SHOW_C_FACTOR;
				break;
			case DISPENSE_CHANGE:
				unsetFlags();
				cout << "You received: $" << gp.dispenseChange() << " as change." << endl;
				cout << "Resetting the pump." << endl;
				cout << "Press any key to continue...";
				cin.ignore();
				cin.get();
				mask |= SHOW_BANNER;
				break;
			default:
				break;
		}
		
		// this might be changed
		if(key.wVirtualKeyCode == PUMP_OUT_ONCE	|| key.wVirtualKeyCode == EXIT
		   || key.wVirtualKeyCode == SEE_DESCRIPTIONS || key.wVirtualKeyCode == REFILL
		   || key.wVirtualKeyCode == INSERT || key.wVirtualKeyCode == LOAD_ONCE
		   || key.wVirtualKeyCode == SEE_MENU || key.wVirtualKeyCode == DISPENSE_CHANGE
		   || key.wVirtualKeyCode == C_FACTOR)
			wasMenuKey = true; // =/
	}
}

/**
 * Convenience method for pausing an independant process without pausing
 * any others. Pauses for a specified amount of milliseconds.
 */
void pause(size_t milli){
	clock_t t1 = clock() + (milli * (CLOCKS_PER_SEC/1000)); // t1 is x milliseconds in the future
	while(clock() < t1); // keep re-evaluating the time until it catches up with t1
}

/**
 * Sets the mask to 0
 */
void unsetFlags(){
	for(size_t i = MAX_BIT + 1; i > 0; i--)mask &= ~(1 << (i - 1)); // Performs bitwise arithmetic to set mask to 0 (to say the least)
}

/**
 * Fills the Console with blank characters, emulating a "wipe"
 * or screen-clearing of the Console screen.
 *
 * To be fair, I had to do some research on how to clear the screen without using
 * system(cls) so I used a similar approach to one submitted on msdn
 */
VOID fadeOut(){
	if(GetConsoleScreenBufferInfo(outputHandle, &csbi)){ // if the program can obtain the Console Screen Buffer Info...
		FillConsoleOutputCharacter(outputHandle, (TCHAR) 32, csbi.dwSize.X * csbi.dwSize.Y, loc, &count); // Fill the
																									      // console with opaque chars
		FillConsoleOutputAttribute(outputHandle, csbi.wAttributes, csbi.dwSize.X * csbi.dwSize.Y, loc, &count );
		SetConsoleCursorPosition(outputHandle, loc); // Sets the cursor to the location loc, relative to he console
	}
	return;
}
Alex Edwards 321 Posting Shark

Oh no O_O

Everything is in global scope, so the program is easily hijackable @_@

>_<

Alex Edwards 321 Posting Shark

I guess this is no longer the ideal gas-pump!

Gas prices (in America) have dropped down to $2.35-$2.75 per gallon O_O

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.