Usage

Usage: chars inputCardsFile inputStringsFile outputFile

Requirements Summary

Create a program that fills in blank strings in an input cards file with an appropriate string from input strings file, and writes the results to an output file. Appropriate means that it satisfies the requirements, not that the sentence will make any sense at all. The selection of the strings to substitute and the order in which to print the strings out is specified in the project description.

Assignment Name

The assignment name for this assignment is: chars

1.chars Against Formatting

The popular game Cards Against Humanity is a horrible game, for horrible people. This assignment, fortunately, can be carried out without playing that horrible game (for horrible people). One interesting aspect of that game, however, is that it is available under a Creative Commons BY-NC-SA 2.0 license. That means you can use, remix, and share the game for free, but you cant sell it without their permission. They kindly asked Please do not steal our name or we will smash you. So we havent. chars Against Formatting is not based on any single game, but any resemblance to games, living or dead, is not just a coincidence.

This assignment requires you to take a card, which is a string with one blank in it, and inserts into those blank spaces an appropriate string from a second file. A blank is represented as sequence of consecutive underscores. For example, consider the following card:

A Lannister _ _ _ _ _ _ pays his debts.

The blank here is a sequence of 6 consecutive underscore characters. Consider the below list of Strings:

Tarth
spoiler
always
Winter
never

Your program should look through this list until it finds the first String with the same length as the blank, i.e., a length of 6, and output a processed card by replacing the blank with that String. In this example, since always is the first String of length 6, the processed card is:

A Lannister always pays his debts.

If no matching String is found, then no processed card should be written to the output file. Each processed card is added to a list of processed cards, and the string used in that processed card is removed from the list of strings. In the above example, always is removed from the list of strings, resulting in:

Tarth
spoiler
Winter
never

2.File Format

2.1 Input Card File

The inputCardsFile is a plain-text file with strings that can be stored in an array of up to 1024 characters (i.e., a string of length 1023, with a at the end), separated by new lines. Any line with no string can be skipped. A card may have whitespace, punctuation, numbers, etc., in its string. A blank is a consecutive sequence of the character _ that is of length 3 or longer. Valid blanks may or may not have spaces on either or both sides in the character array.

Not every line from the input file is guaranteed to have a blank (i.e., a sequence of underscore characters) in it. If a card does not contain a blank or contains more than one blank, the card should not be added to the list of unprocessed cards. Duplicate cards are permitted in inputCardsFile and the list of unprocessed cards.

As always, your program should be robust to files that are empty, or that do not match the input formats.

For example, consider an input cards file containing:

A Lannister _ _ _ _ _ _ pays his debts.
A Lannister always pays _ _ _ debts.
A Lannister always pays his debts.
A Lannister always _ _ his debts.

The first two lines are valid cards, but the last two cards are invalid.

2.2 Input String File

The inputStringsFile is a plain-text file with whitespace separated strings. Each valid string read from the sting file will be stored in a list of strings. Any line with no strings can be skipped. Any string with fewer than 3 characters should not be added to the list of strings. Input strings can be any valid ASCII string of alphanumeric or punctuation characters (i.e., isalnum() or ispunct() returns true), and invalid strings should not be added to the list of strings. Strings can consist of values that seem like another type (e.g., integer and floating-point), but they should be read in as strings, not numeric values. Duplicate strings are permitted in inputStringsFile and the list of strings. Strings in the inputStringsFile will never contain special characters (e.g., underscores, dashes).

2.3 Output File

The outputFile is a plain-text file that will consist of all successfully processed cards. Each processed card should be written to the outputFile followed by a new line. If there are no processed cards, your program should create an empty output file with name provided from the command line arguments.

The processed cards stored in the output file should be ordered by length (from shortest to longest). If two processed cards have the same length, then the one that appears first in the inputCardsFile should be written first to the outputFile.

Recommendation: As each card is processed, insert the card into the list processed cards lists in the correct position based on the length of the processor card. If your program inserts card in the correct position within the list, your program does not need to implement a sort algorithm..

3.List Structures

To keep track of unprocessed cards, strings, and processed cards, you must use doubly-linked lists. If you do not use lists for this assignment your grade will automatically be a 0. You must also use dynamic memory allocation to maintain your lists, and to maintain the data inside each list node. Your program should create the following lists:

  • Unprocessed cards
  • Unprocessed strings
  • Processed cards

The following struct and typedef definitions must be used for the doubly-linked list. DO NOT make modifications to these data structures in your assignment.

typedef struct DListNode_struct {
char *str;
int blankIndex;
int blankLength;
struct DListNode_struct *next;
struct DListNode_struct *prev;
} DListNode;

typedef struct DList_struct {
int size;
DListNode *head;
DListNode *tail;
} DList;

str is a pointer pointing to a valid string read from a file. blankIndex is the index of the first underscore character of a valid blank in a string (i.e., a character array). blankLength is the length of a blank. next is a pointer pointing to its next node, and prev is a pointer connecting to its previous node. size describes the size of a list (i.e., the number of nodes in a list). head is a pointer pointing to the first node in the list, and tail is a pointer pointing to the last node in the list.

The following functions must be implemented for the doubly-linked list using the provided function declarations. DO NOT make modifications to the function names, return types, parameter names, or parameters types.

/*******************************************************************************************/

/*
* File: dlist.h
* Author: Your Name
* NetID: Your NetID
* Date:
*
* Description: (enter your description of what this file does)
*
*/

/*******************************************************************************************/

/* Note: comments in this file thus far are insufficient for a perfect grade in comments */

/*******************************************************************************************/

#ifndef DLIST_H
#define DLIST_H

/*******************************************************************************************/

/* if the str does not contain a blank, then blankIndex and blankLength should be -1 */
typedef struct DListNode_struct {
char *str;
int blankIndex;
int blankLength;
struct DListNode_struct *next;
struct DListNode_struct *prev;
} DListNode;

typedef struct DList_struct {
int size;
DListNode *head;
DListNode *tail;
} DList;

/*******************************************************************************************/

/* creates a new list and initializes the head/tail */
void DListConstruct(DList* list);

/* removes all items from the list, deallocating each item */
void DListDestruct(DList* list);

/* inserts newNode after the given currNode */
void DListInsertAfter(DList* list, DListNode* currNode, DListNode* newNode);

/* inserts newNode before the given currNode */
void DListInsertBefore(DList* list, DListNode* currNode, DListNode* newNode);

/* return the first list node to match key */
DListNode* DListSearch(DList* list, char* key);

/* remove the list node (if it is a member of the list) */
void DListRemove(DList* list, DListNode* currNode);

/*******************************************************************************************/

#endif // DLIST_H

The functionality specific to these data structures should be implemented within their own set of C source and header files named dlist.h and dlist.c. You may add more functions that process lists, or creates individual nodes, though you should create them inside dlist.c, so that your code can compile and run with the original dlist.h header.

Do not modify the dlist.h header file, except to change your name and details for the final release. You may not change the above function signatures in any way, and you should ensure that you implement each of the above functions for your alpha release inside dlist.c. You can declare new functions inside of dlist.c as long as those functions are fully implemented without the need to include other headers from your solution.

Reminder: If your program does not use a doubly-linked list as indicated in the program assignment, you will receive 0 points for the assignment.

4.Notes on Blank Replacement Algorithm

Once a string is used to process a card, it should be removed from the list of strings. Duplicate strings are permitted in the list of unprocessed strings. If when processing a card no suitable string is found to replace its blank, then it should not be placed in the list of processed cards. A corollary to this is that if the list of unprocessed strings is depleted to size 0, then the remaining unprocessed cards will not be placed in the list of processed cards

5.Recommended Functional Decomposition (Top-Down Design)

5.1 Card Inputting and Processing

As each card is read from the inputCardsFile, your program can check to see whether it has a blank, where the blank is located, and how long the blank is, etc.

As each card is processed while playing the game, you will update its node information, replacing the original string with a new string. The string values of each node should be dynamically allocated, and should be of the minimal length needed to store the string (including its terminating null character). The data containing the old string should be freed after the new string is created and associated with the list node.

Recommendation: Create files cardprocessing.h and cardprocessing.c to incorporate logic and functions to read in cards, and fill blanks with strings. Do not put this functionality in main, or you will receive deductions for bad design.

5.2 String Inputting and Processing

As each string is read from the inputStringsFile, your program can confirm its length is greater than or equal to 3, confirm it only contain alphanumeric and punctuation characters, and add ach valid string to the list of string.. Use the same data structures (DListNode and DList) to store these strings, but set the data values associated with blanks to -1 (i.e., blankIndex and blankLength).

Recommendation: Create files stringprocessing.h and stringprocessing.c to incorporate logic and functions to read in strings. Do not put this functionality in main, or you will receive deductions for bad design.

5.3 Output File Processing

Once you have processed all the cards, write the output file based on the list of processed cards. You need only write the successfully processed cards to the output file.

Recommendation: Create files output.h and output.c to incorporate logic and functions to write out the processed cards. Do not put this functionality in main, or you will receive deductions for bad design.

5.4 What goes in main

In the main() function, you should instantiate your lists, check the command line arguments, and return a usage statement if needed. Put all the logic and error handling for file I/O, initialization of lists, etc., in your functions as defined in your other files, and in the order that makes sense. You want your main to be very simple, so that if you want to reuse your code, you can do so without a main() function.

Recommendation: Instantiate three lists on the stack in your main() function, pass these lists (by address) to the various functions, and destroy the lists before you exit your main() function. All the data of these three lists will be allocated on the heap. Thus, your DListDestruct() function will only free the data in the list (i.e., the list nodes and data in the list nodes), not the list itself.

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.