CSC 161 Schedule Readings Labs & Projects Homework Deadlines Resources

Rack-O®

Summary
You build a program to support playing a simple strategy game.
Objectives
  • Practice using list-backed stacks
  • Contrast list, stack, and array suitability
  • Reinforce memory-management knowledge
  • Verify user input

Introduction

Game Overview

The board game Rack-O is a simple game requiring players to sort a fixed-length rack of cards through replacements from a randomized sequence. With gameplay for 2–4 players, it debuted in 1956 and remains fun and popular today.

Game play involves a deck of cards numbered 1–60. To begin, these cards are randomly shuffled. Each player has a rack in which ten cards are placed into slots. At the start of the game, the cards are dealt from the shuffled deck to alternating players' racks in increasing slot order. The remaining undealt cards get placed into a stock pile. The top card from the stock pile is turned over so it is visible and placed into the discard pile

The object of the game is to be the first player to obtain a rack with all ten cards appearing in increasing sorted order.

The game is played by alternating turns. A player chooses a card from either the stock or discard pile. If the player chooses from the discard pile, they must use it to replace an existing card in their rack: the card removed from the rack then goes on the top of the discard pile and the new card is inserted in its place. If the play instead chooses from the stock pile, they may either return that card to the discard pile or use it to replace a card in their rack.

In the full game, this play constitutes a round, with subsequent scoring based on the card placement in the rack. However, in our dimunition of the game, such play will alternate until one player has simply Gone Rack-O! (achieved a sorted rack).

Program Structure

The astute reader will have noticed the opportunities for use of the following data structures:
Stacks:
Only the top-most card is accessible from the stock and discard piles, and the length is variable. Therefore, these collections will be represented in your program with stacks backed by linked lists.
Racks:
The racks have a fixed length and are accessed by an arbitrary index (rather than sequentially). This precisely fits the usage pattern of arrays.

Your game play will only support two players, and will not involve any sort of intelligent computer player. Instead the program will keep track of the piles and racks, asking the players for their choices and displaying the relevant information as it arises.

Assignment

As one might imagine, correctly using these ideas requires attention to a number of practical details.

Card Stacks

To support the two card piles, you will need to implement and test a simple stack.

Implementation

Place the following (with documentation, include guards, and the necessary headers) declarations into cardstack.h and implement the corresponding procedures in cardstack.c.

typedef struct node {
  int card;
  struct node * next;
} node_t;

bool
isEmpty (const node_t * stack);

void
push (int card, node_t ** p_stack);

int
pop (node_t ** p_stack);

int
top (const node_t * stack);

void
clear (node_t ** p_stack); // Empty the stack, freeing all memory             

Your documentation should clearly identify necessary preconditions, and your implementation should verify those preconditions with assertions.

If any standard library routine fails, you may use perror to print a contextualized error message and exit the program from within your library.

Testing

Use preprocessor macros to contruct a simple, but comprehensive test suite for your stack.

The stack library cardstack.c will eventually be compiled separately and linked with your game program. However, rather than create a separate driver program (that is, a C file with a main procedure), add a main to the bottom of cardstack.c with a conditional inclusion dependent on the TESTSTACK symbol to indicate testing. For example:

#ifdef STACKTEST
// Test helper routines here

int
main (void)
{
// Test program here
} // main
#endif

One would then be able to test your stack by compiling with something like the following:

clang -Wall -DSTACKTEST -o stacktest cardstack.c

Makefile

Create a Makefile for your assignment that includes the following targets:

cardstack.o
Builds that object file from your cardstack.c. Note that none of the testing routines should be present in the object.
stacktest
Builds that executable program from your cardstack.c. Note that all of your testing routines should be present in the program and run when the program is executed.
permute.o
Builds that object from the provided code permute.c (which relies on permute.h).

Your Makefile should separate out variables CPPFLAGS, CFLAGS, and LDFLAGS that are subsequently used in the appropriate target rules.

You may want to add another variable FLAGS (initially empty), as follows

# Optional, universal  flags to use in all build commands (compile, link, etc)
FLAGS=
that is used in all rules, so that you can incorporate the Address Sanitizer through that variable, i.e.,
make FLAGS=-fsanitize=address

Game Play

Complete the functions in racko.c to implement the full game play. Note that the main function has been completed for you, but the rest of the functions are provided as stubs. Completing these stubs should allow you to run the game, as demonstrated in this transcript. Bad user input should be handled robustly, as demonstrated in another transcript.

The following notes may help guide your thinking about the program structure:

Makefile revisited

Add the following targets to your Makefile, including any necessary dependencies:

racko.o
Builds that object file from your racko.c program.
racko
Builds the executable game program by linking the necessary object files.

Grading

In addition to the general grading guidelines for evaluation, the assignment is worth 45 points.

A five point penalty will apply to any submission that includes personally identifying information anywhere other than the references/honesty file.

To pass the assignment (with a grade of C or better), tests must demonstrate a clean diagnosis from AddressSanitizer.

Beyond those provided in the starter file, additional global variables may not be used anywhere; use of any additional global variable will result in a grade of zero.