2006-05-03

Mu Torere, Undo

Disclaimer: Before going on to describing the Undo functionality, here is the reason for the program using a base and derived class and for storing the old information as a CGame object.
It looked like a good idea at the time, OK?
Seriously though, the reasoning for the Queue class from the undo calls was to allow updating the Queue class for further functionality in the future and to avoid the temptation of putting queue functionality in the undo calls.
As for using an entire CGame object, well consider that a placeholder. To minimize the run-time footprint one can extract the bare minimum data needed to represent the game and then apply this data later. But that can be left for future updates.

Step 13.
The Base Queue.
The CCircleQueue class uses several named constants to improve program readability.
First off are the upper and lower limits to size of the queue. Now it seems to make very little sense to worry about limits while talking about a static queue. Except, suppose the decision on the queue size was re-considered and a change was requested. In this program the change can be implemented in the registry and avoids the re-compiling that a hard-coded number would need.
While the upper limit is pretty much arbitrary, the lower limit of 2 was chosen with the view to keep the special cases within reason.

The data structure is relatively simple; one pointer going forward (into the future), one pointer going back (back to the past), and one pointer to the data. The class uses two pointers to this data structure for its purposes.

The constructor simply sets the structure pointers, topPoint and stopPoint, to NULL and sets the bookkeeping integer, qSize, to 0. The queue will be created seperately. The destructor uses a seperate function to actually de-allocate the queue.
void CCircleQueue::Release(void)
Pre-Conditions:
That topPoint is NOT NULL.
That topPoint->forward is NOT NULL.
While a temp pointer is NOT equal to topPoint AND is NOT NULL,
{
Copy the temp pointer to another pointer for deletion.
Move the temp pointer over to temp->forward.

Process the copied pointer by deleting its data pointer if NOT NULL and by deleting this pointer.
}

Delete topPoint->data, if NOT NULL.
Delete topPoint.

Finally, set stopPoint to NULL


The Queue creation begins with;
bool CCircleQueue::newQueue(const int& levels)
Pre-Conditions:
That levels is between CCircleQueue::Q_LLIMIT and CCircleQueue::Q_ULIMIT inclusively.
Assign an integer variable, counter, to levels (or the value of levels that is within bounds).

Create 2 elements for the initial Queue using topPoint and a temporary pointer. If this allocation fails, return false.
Assign the forward and back pointers.

Assign CCircleQueue::Q_LLIMIT to qSize.
While qSize is less than counter AND less than or equal to CCircleQueue::Q_ULIMIT,
Add a queue element using growOne(). If this fails at any point( NOT equal to CCircleQueue::QGROW_GOOD), break out of loop. (qSize is incremented within growOne().)

In any case, return true.


The function used for growing the Queue builds on an already initialized queue. At this time the only point at which the queue will grow is from topPoint (which is known) and will only use the forward structure pointer. This means that (if the queue is already in use and has data) the historical data structure can still be reached.
CCircleQueue::qReport CCircleQueue::growOne(const CCircleQueue::qPoint& addTo)
Pre-Conditions:
That topPoint is NOT NULL.
That topPoint->forward is NOT NULL.
That addTo equals CCircleQueue::QPOINT_FIRST.
Attempt to allocate a queue element. Set its data pointer to NULL.

Insert this element into the queue forward of topPoint.

Return CCircle::QGROW_GOOD.
There are two distinct error returns from this function. CCircleQueue::Q_ERROR is used on the event of a mal-formed queue, while CCircleQueue::QGROW_FAIL reports a failure (possible memory shortage) in adding additional queue elements.

The queue resetting function is as follows;
void CCircleQueue::resetQueue(void)
Pre-Conditions:
That topPoint is NOT NULL.
That qSize is between CCircleQueue::Q_LLIMIT and CCircleQueue::Q_ULIMIT inclusive.
A while loop is used to visit each element in the queue, delete any allocated CGame pointer and set those pointers to NULL.
NOTE:If the loop eve finds that the next element in the queue is missing (NULL), the program SHOULD exit.

Finally, stopPoint is set to NULL.
The program does use a 'called-out-of-order' condition statment before the preconditions, if qSize is less than CCircleQueue::Q_LLIMIT then the create Queue function is called with the specification of CCircleQueue::Q_LLIMIT.

Both the push() and the pop() functions are overloaded. But the basic push() function is;
bool CCircleQueue::push(const CGame& data, const CCircleQueue::qPoint& addTo)
Pre-Conditions:
That topPoint is NOT NULL.
That qSize is between CCircleQueue::Q_LLIMIT and CCircleQueue::Q_ULIMIT inclusive.
That addTo equals CCircleQueue::QPOINT_FIRST.

Again a 'called-out-of-order' condition statment before the preconditions, if qSize is less than CCircleQueue::Q_LLIMIT then the create Queue function is called with the specification of CCircleQueue::Q_LLIMIT.
Attempt to allocate a new CGame pointer for the queue. Set this allocation to data.

If this is the very first data pointer for the queue (topPoint->data == NULL),
{
(stopPoint should be NULL at this time.)

Set topPoint->data to the new CGame allocated pointer.
Set stopPoint to topPoint.
}
Else if the queue is full up (the pointer forward of topPoint is stopPoint),
{
(There is a valid pointer for stopPoint->forward.)

Set topPoint to stopPoint.
Move stopPoint to stopPoint->forward.

Delete the CGame allocation (if it exists) in the current topPoint->data.
Set topPoint->data to the new CGame allocated pointer.
}
Otherwise,
{
(stopPoint should be a valid pointer.)
(topPoint->forward should exist.)

Move topPoint to topPoint->forward.
Delete the CGame allocation (if it exists) in the current topPoint->data.
Set topPoint->data to the new CGame allocated pointer.
}
After all that return true.
And the pop() function is;
void CCircleQueue::pop(CGame& data, const CCircleQueue::qPoint& getFrom)
Pre-Conditions:
That topPoint is NOT NULL.
That qSize is between CCircleQueue::Q_LLIMIT and CCircleQueue::Q_ULIMIT inclusive.
That getFrom equals CCircleQueue::QPOINT_FIRST.
If stopPoint is NULL, there is nothing left to pop.

If this is the last one in the queue (topPoint == stopPoint),
{
(topPoint->data should exist.)

Set returned data to topPoint->data.
Delete topPoint->data and set it to NULL.

Set stopPoint to NULL.
}
Else,
{
(topPoint->data should exist.)
(topPoint->back should exist.)

Set returned data to topPoint->data.
Delete topPoint->data and set it to NULL.

Move topPoint to topPoint->back.
}
The test for popping data is check if topPoint is NOT NULL AND that stopPoint is NOT NULL.

No comments: