CS102 Lecture Notes -- The BottleCap class

James S. Plank

April 3, 2007.

Today in class I showed a nice way to do a bottlecap code generator/evaluator using the object oriented methodology.

First, in bcap.h, we have the specification for a BottleCap class:

#include <iostream>
#include <vector>
using namespace std;

class BottleCap {
  public:
    BottleCap(int codelength, int nchars, vector <int> rewards);
    int Evaluate(string code);
    string Generate(int value);
  private:
    int EvalFxn(string code);
    int length;
    int numchars;
    vector <int> rewards;
};

Note, there are three public methods -- the constructor, Evaluate() and Generate(). The constructor takes three arguments:

The Evaluate() method takes a string as its argument. This is the code. If the code is illegal (wrong number of characters or wrong characters), Evaluate() returns -1. Otherwise, it returns rewards[c], where c is the value of the function applied to the code.

The Generate() method returns a random code whose function is equal to the value argument.

The BottleCap class has three private variables, which are copied from the constructor arguments when the constructor is called. It also has a private method called EvalFxn(). This takes a code as its argument and returns the value of the function applied to the code. This method is private because it is used in the implementations of Evaluate() and Generate(), but we do not wish to allow users of the BottleCap class to use it.


Implementation

The BottleCap class is implemented in bcap.cpp. It is straightforward. All of the public methods do some error checking on their arguments. You'll note, though, that EvalFxn() does not do any error checking. That is because it is only called by Evaluate() and Generate() and is guaranteed to be called on legal codes.

The only subtle code is in Generate() -- note that I use new to create a C-style string of the correct size. When I return, I create a C++ string from the C string and call delete to deallocate the memory that I created with the new call. This is because otherwise, that memory will become "dead" -- no one will ever use it. So as not to waste memory, I call delete.


Generating Codes

A very simple bottlecap code generator is in bcap-gen.cpp. I'll include it below:

#include <iostream>
#include <vector>
#include "bcap.h"
using namespace std;

#define L 15
#define M 17

int main(int argc, char *argv[])
{
  BottleCap *bc;
  vector <int> rewards(M);
  int i, val, ncodes;
  string code;

  if (argc != 3) {
    cout << "usage: bcap-gen number-of-codes value\n";
    exit(1);
  }

  ncodes = atoi(argv[1]);
  val = atoi(argv[2]);

  for (i = 0; i < M; i++) rewards[i] = 0;
  rewards[3] = 3;
  rewards[10] = 10;
  bc = new BottleCap(L, M, rewards);

  srand(time(0));

  for (i = 0; i < ncodes; i++) {
    cout << bc->Generate(val) << endl;
  }
}

It first checks argc to make sure that it has been called with the correct number of arguments. Then it initializes the integers ncodes and val from the command-line arguments. It does this with atoi(), which converts a C-style string to an integer.

Next, it creates an instance of the bottlecap class using new and the constructor. Before doing that, it creates its rewards vector, which is all zeros, except for a 3 in index 3 and a 10 in index 10. Finally, it generates ncodes codes whose values are equal to val.

Let's see it compiling and running:

UNIX> g++ -o bcap-gen bcap-gen.cpp bcap.cpp
UNIX> bcap-gen
usage: bcap-gen number-of-codes value
UNIX> bcap-gen 5 10
ICNHAAFDPGQEKKJ
INCNJQHEAGLNLCP
MBEFPFLJDONLJQB
IPINMGBQKKJKMLA
KLCLEJHHCLBEPBJ
UNIX> bcap-gen 5 3
QMFQFCBJHDGCKBQ
GCPMEJEBKPAAFDP
QAGCMFDCCGMNPDC
HFMFNLKPMHBABCN
HMBNAIIHEGIJFKN
UNIX> 

Evaluating Codes

A very simple bottlecap code evaluator is in bcap-eval.cpp. I'll include it below:

#include <iostream>
#include <vector>
#include "bcap.h"
using namespace std;

#define L 15
#define M 17

int main(int argc, char *argv[])
{
  BottleCap *bc;
  vector <int> rewards(M);
  int i;
  string code;
  int val;

  for (i = 0; i < M; i++) rewards[i] = 0;
  rewards[3] = 3;
  rewards[10] = 10;

  bc = new BottleCap(L, M, rewards);

  if (argc == 1) {
    while (!cin.fail()) {
      cin >> code;
      if (!cin.fail()) {
        val = bc->Evaluate(code);
        cout << "Code " << code << " is worth " << val << " points.\n";
      }
    }
  } else {
    for (i = 1; i < argc; i++) {
      code = argv[i];
      val = bc->Evaluate(code);
      cout << "Code " << code << " is worth " << val << " points.\n";
    }
  }
}

Note first how it creates the same instance of the BottleCap class as bcap-gen.cpp. Then it evaluates input dependingo on its command line arguments. If there are none, then it reads from standard input using cin. If however there are command line arguments, then it assumes that they are codes and evaluates them. Note how this is neat because you can test it by piping the output of bcap-gen into it to test:

UNIX> g++ -o bcap-eval bcap-eval.cpp bcap.cpp
UNIX> bcap-eval QMFQFCBJHDGCKBQ MIPBCOAHGHGACLN AAAAAAAAAAAAAAA Thor
Code QMFQFCBJHDGCKBQ is worth 3 points.
Code MIPBCOAHGHGACLN is worth 10 points.
Code AAAAAAAAAAAAAAA is worth 0 points.
Code Thor is worth -1 points.
UNIX> bcap-gen 5 10 | bcap-eval
Code JMIKQGOADOCQIPF is worth 10 points.
Code LNNFKDPANGJCLOH is worth 10 points.
Code GQIMJFQJGNENGCI is worth 10 points.
Code FEDNHBPCQDPNJPO is worth 10 points.
Code GBGADEQBLOBHIED is worth 10 points.
UNIX> bcap-gen 5 3 | bcap-eval
Code BJEEBDNQPQHQFDL is worth 3 points.
Code CBJIPAJLCBPDLJB is worth 3 points.
Code PCCHQGOKHQHFLBO is worth 3 points.
Code KQPQOBOAPCDOLNA is worth 3 points.
Code FIBCHFAHCBJLBIM is worth 3 points.
UNIX> bcap-gen 5 12 | bcap-eval
Code NKADIFJHDIONPFK is worth 0 points.
Code IOOGBMOBBOQDJOG is worth 0 points.
Code AFOPAMLQCKKAOQE is worth 0 points.
Code ACOHFBBGIEHDKQP is worth 0 points.
Code LGENNCEKNIBNQBP is worth 0 points.
UNIX>