Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

CMPT 214– Programming Principles and Practice

Assignment 5

General Submission Instructions

• Assignments must be submitted using Canvas.

• Programs must be written in C conforming to the C11 standard. Everything we teach you is compli- ant with the C11 standard. Things we haven’t taught you might not be, so use only the features of C that we have taught. If you try things you find on other websites (where you should definitely not be looking for solutions) they may not be C11 compliant. Everything you need to solve programming problems can be found in the course materials, or in the assignment itself.

• Include the following identification in a comment at the top of all your code files: your name, NSID, student ID, instructor’s name, course name, and course section number.

• Late assignments are accepted with a penalty depending on how late it is received. Assignments are not accepted more than 2 days late (the assignment submission will close 48 hours after the due date passes and you will not be able to submit). See the course syllabus for the full late assignment and assignment extension policies for this class.

VERY IMPORTANT: Canvas is very fragile when it comes to submitting multiple files.  We insist that you package all of the files for all questions for the entire assignment into a single ZIP archive file.  This can be done with a feature built into the Windows explorer (Windows), or with the zip terminal command (LINUX and Mac), or by selecting files in the Mac finder, right-clicking, and choosing "Compress ...".  We cannot accept any other archive formats other than ZIP files.  This means no tar, no gzip, no 7zip ( .7zip), and no WinRAR ( .rar) files. Non-ZIP archives will not be graded.

• Instructions on "how to create zip archives" can be found here: https://canvas.usask.ca/courses/62306/pages/how-to-zip-slash-compress-your-files

Background

New LINUX/UNIX Commands

A very useful command on LINUX/UNIX is the man command. It is discussed in the textbook in Sections 5.3, 8.7.3, and 17.2. It is very helpful in determining what functions exist in a family of C library functions, what arguments a particular function takes, what it returns, how it operates, what return value it has, what type of errors it can generate, etc. There are also man pages for standard commands that you input the shell.

Commands that we will be using in this assignment are file and nm. The file command reports what type of information a file contains, while nm lists the symbols that are present in an object module.  (An example of an object module is a .o file.) Read about these two commands by logging in to tuxworld and typing

and

Environment Variables

The LINUX operating system needs to know where dynamically-linked shared libraries are located; i.e. in which directories to look for shared libraries at runtime. On LINUX/UNIX the set of locations (directories) is provided by the setting of the environment variable LD_LIBRARY_PATH. The setting of the variable is a colon-separated list of directories. The directories are searched in the order in which they are specified. You can find out the current setting of LD_LIBRARY_PATH by giving the command

on tuxworld. Note that environment variables are discussed in Chapter 22 of the text, and LD_LIBRARY_PATH is briefly described on page 277 (just prior to section 22.5).

Question 1 (11 points):

Purpose: To practice building dynamic and static libraries, and writing a makefile to automate building libraries.

For this question you will build both a dynamically-linked shared library and statically-linked library from source code provided to you. The source code is for two functions. One, rand_num(), generates a pseudo-random number in a specified range. The series of pseudo-random numbers generated by the function will always be the same, however. This is useful if one wishes to have a program use a consistent sequence of pseudo-random numbers, such as when testing or debugging.  However, the series of pseudo-random numbers can be altered by changing the seed for the series.  To obtain a series of pseudo-random numbers that is different each time a program is run, one can change the seed each time the program is run.  The second function provided for you, rand_init(), does this. See the comments in rand_num .h for a description of the functions, the arguments they take, and the values they return.

If you are interested in the C library functions used by rand_num() and rand_init() you are welcome to check the man pages for gettimeofday() and random().

Some of the tasks below require that you record a log of your activities. Create that log in a file named asn5q1 .log.

Your Tasks

• From the LINUX terminal (BASH shell), compile rand_num .c creating file rand_num_s .o. Then create a static library named librn .a from rand_num_s .o. You do not have to record anything in asn5q1 .log as part of this task.

• To verify that your static library is correctly constructed, issue the following commands and record the commands and their output in asn5q1 .log.

• From the LINUX terminal (BASH shell), compile rand_num .c creating file rand_num_d .o.  Re- member that you will need the -fpic option when compiling. Then create a shared library called librn.so from rand_num_d .o.  Note that the man page for gcc advocates that you give -fpic when creating the shared library (in addition to -shared). You do not have to record anything in asn5q1 .log as part of this task.

• To verify that your shared library is correctly constructed, issue the following commands and record the commands and their output in asn5q1 .log.

• Create a makefile that will automate the creation of librn .a and librn .so. The makefile is to be able to build four main targets:

librn.so: a dynamic (shared) library containing the rand_num module;

librn .a: a static library containing the rand_num module;

librn: target of a phony rule that causes both of the above libraries to be built this should be the default target for the makefile;

clean: target of a phony rule that removes all makefile targets (causing everything to be rebuilt on the next build).

The makefile may have additional rules to build intermediate targets.  Once your makefile is working correctly, make a copy of it called Makefile_q1. You do not need to record anything in asn5q1 .log for this part of Question 1.

Implementation Notes

Do not modify the contents of rand_num .c or rand_num .h in any way.

Testing

Make sure to test that your makefile works correctly. However, you do not need to submit any proof of testing.

Question 2 (26 points):

Purpose: To practice writing a complete C module and to practice writing makefiles to automate building a project from multiple modules and a library.

Overview

You will write a module for a game called ShipBattle. This module will encapsulate and abstract the ShipBattle game board.

ShipBattle is a game where enemy ships are hidden on a grid. The player guesses grid positions to shoot at and the game lets the player know whether their shot hit the ship, missed the ship, or hit a position that was previously shot at. A ship sinks as soon as it is hit. The game ends either when the player quits or when all ships have been sunk.

The rules for ShipBattle are different from a similar well-known game, Battleship.   In Battleship, ships have different sizes, occupy more than one grid position and require multiple hits to sink.  In ShipBattle, every ship occupies only one grid position and only requires one hit to sink.  Moreover,

Battleship involves two players. ShipBattle is a solitaire game for one player.

Provided with this assignment are two modules:

gameplay.[ch]: This module contains the gameplay logic. You can look at the code and see how the game works. Note that the gameplay module requires librn which you built in question 1. Thus you’ll be linking your program against your librn .so.

shipbattle.c: This module contains the main() function but does very little. It simply initiates game- play by calling a function in the gameplay module, and then reports on the result of the game.

Your task is to write the gameboard module for this program. The gameboard module manages the game state (where the player has shot, whether each shot was a hit or miss) and the positions on the gameboard where there are ships to be sunk. You’ll eventually discover that the gameboard module will also require functions from librn.

The complete shipbattle program executable is built from the modules gameplay, gameboard, and shipbattle, and must be linked with librn.

Your Tasks

1. Write a Makefile to build the ShipBattle program from its three modules and links it with librn. Start with your makefile from question 1 which already has rules to build librn and already has a clean rule. For the makefile to work as intended, the makefile, rand_num .c, rand_num .h, and all of the code files for the ShipBattle program must be in the same directory.

(a) Add a target all which depends on targets shipbattle (the executable program) and librn; make this the default target.

(b) Add a shipbattle target that builds the program executable, as well as any dependent target rules needed to build the modules. Since both a static and a dynamic versions of librn are available, this target will link to the dynamic library by default, which is what we want.

(c) Modify the clean rule so that it deletes the shipbattle executable in addition to all .o files and compiled libraries.

(d) Where appropriate, tag targets as phony rules.  If there are implicit rules for linking, make sure that LDFLAGS is appropriately defined.

(e) Similarly, if there are implicit rules for compiling, make sure that CFLAGS is appropriately defined.

(f) When your makefile is working to your satisfaction, make a copy of it in file Makefile_q2.

2. Write the gameboard module. The gameboard module will implement functions that are called by the gameplay module. The specification of the module is given below – see “Gameboard Module Specification“ .

3. Document gameboard .h and gameboard .c with appropriate inline comments describing program logic, and block comments describing functions purpose, parameters and return values as per previously established conventions. Also document your definition of the Gameboard type so that it explains how the data is represented.

Gameboard Module Specification

Data Structures

BoardPosition Type

The gameplay module references a type called BoardPosition. This is an abstraction of a game board position. It must be defined by the gameboard module you are writing. Since the gameplay module depends on specific definition of the BoardPosition type, you must use this for its definition:

Gameboard Type

The gameboard module manages a data type called Gameboard which stores the following information:

• The width and height of the game board (integers, each of which must be no less than 5 and no more than 26).

• The number of hidden ships (a random integer not less than 5 and not more than 10).

• The current number of shots taken by the player in the game (initially zero).

• The grid positions of the ships on the game board.

• The game state: this consists of storing the grid positions at which the user has taken shots, and whether they were a hit or a miss (initially, no positions have been shot at, and there are no hits or misses).

The design of the Gameboard type is up to you. Consider carefully the best way to represent and store each data component of the Gameboard — you’ll be evaluated on the appropriateness of your choices. Keep in mind that the size of the board is not known until runtime!

Operations

The gameboard module must provide the following functions that operate on instances of the Gameboard type:

•  create_gameboard():  This function must accept two parameters:  the width and height of the game board (each minimum 5, maximum 26).  It should return a pointer to a newly created and initialized Gameboard that is ready to be used to begin a game of ShipBattle. This includesrandomly determining how many ships there are (there should be no less than 5 ships and no more than 10 ships), randomly determining the location of those ships. If a random position for a ship is the same as the already-determined random position for another ship, choose a different random location for the current ship (i.e. each ship’s random position must be unique and valid for the given board size).

• print_game_state(): Given a pointer to a Gameboard instance, this function must print to stdout the state the game. It should display a grid of characters using the character ’ . ’ to represent a position that the player has not shot at, the character ’o’to represent a position that the player has shot at and missed, and the charcter X’to represent a position that the player has shot at and hit. See the sample run, below, for an example how this might be formatted.

•  check_for_hit():  This function must accept a pointer to a Gameboard and a BoardPosition. If the board position is valid, this function increments the number of shots taken in the given Gameboard.  Then it must return one of three named macros:  RESULT_HIT if the given board position contains a ship and was not shot at before, causing the ship to sink; RESULT_MISS if the given board position does not contain a ship and was not shot at before; and RESULT_REPEAT if the given board position was previously shot at, already sinking any ship that might have been there. Note that it is up to you to define these named macros in such a way that the gameplay module can see and use them.

• record_hit(): This function must accept a pointer to a Gameboard and a BoardPosition. If the board position is valid, this function must record in the game state that a hit has occurred at the given game board position so that the new hit is reflected in the output of subsequent calls to print_game_state().

• record_miss(): This function must accept a pointer to a Gameboard and a BoardPosition. If the board position is valid, this function must record in the game state that a miss has occurred at the given game board position so that the new miss is reflected in the output of subsequent calls to print_game_state().

•  is_victory(): This function must accept a pointer to a Gameboard. If all ships have been sunk by the player (can be checked by comparing the game state to the ship locations), this function

should return non-zero; otherwise, it returns zero.

The above specification of operations intentionally describes what each function must do, but it doesnt specify how it is done (which is normal for specifications!). The implementation details of each function are up to you and will depend heavily on your design of the Gameboard data structure. As long as you adhere rigidly to what each function expects as input and what it is expected to return, it should work perfectly with the gameplay module.

Implement each function to do exactly what is described! No more, no less. For example, other than print_game_state(), all game output messages are already present in gameplay .c and are not to be output by gameboard .c.

Implementation Notes

You may not modify anything in any of the provided .c or .h files. The only exception to this is that you may temporarily comment out the rand_init() line in shipbattle .c.  Doing so prevents seeding of the random number generator so that every time you run the program you will get the same sequence of pseudo-random numbers useful for testing! Make sure you change it back before submitting your assignment solution.

• The players enter board positions as a letter followed by a number with no space in between (see the read_board_position() function of the gameplay module) where the letter indicates a row, and the number indicates a column. The read_board_position() function will convert the letter-number pair to a row/column index for you and store that in a BoardPosition, so you don’t have to worry about that conversion.  Note that board position A1 entered by the player

is converted to row 0, column 0, and, for example, board position C5 entered by the player is converted to row index 2, column index 4.

Testing

Test your ShipBattle program by building it with your makefile and then running the shipbattle executable. You do not have to submit any proof of testing.

Normally, shared libraries would be placed in some system wide area.  Unfortunately, this will not be the case for librn .so.  To be able to test your dynamically-linked version of treasure you will need to set LD_LIBRARY_PATH to indicate where (in which directory) to find librn .so. Assuming that librn .so is in your current working directory, give the following command to the shell:

You will only need to give the above command once each time log in to tuxworld.

Sample Run

Here s the current board . So far you ve sunk 0 of 9 ships .

.

.

1     2 3    4     5

. .     .     .     .

. .     .     .     .

C

D

. .     .     .     .

Enter a board position to attack ( e . g . B12 ): B4

It s a hit !

Press ENTER to continue or type the letter q or Here s the current board . So far you ve sunk 1 of

Q followed by enter to quit . .

9 ships .

Q followed by enter to quit . .

9 ships .

A

B

C

D

E

1

.

.

.

.

.

2

.

.

.

.

.

3

.

.