For this exam, you are asked to implement components of the game battleship. If you are unfamiliar with the game, you can read about the rules (https://www.thesprucecrafts.com/the-basic-rules-of-battleship-411069) or play an online version (https://www.mathsisfun.com/games/battleship.html).
You will not implement a complete game but only some elements that are inspired by this game. Specifically, there is no opponent. Instead, you will place ships on a game board and then shoot at those same ships. Yes, this is not a very appealing game but our goal is to test you on your ability to implement the different core functionalities.
The constraints of the general setup are illustrated below. Note that ships can touch, but not overlap. In this example, the board has 7 rows and 10 columns, with three ships (of length 2, 4 and 6) placed in valid positions. This is just an example; more details on what board and ship sizes you need to support are provided when we explain the specific tasks.
We provide you with starter code. To get this code, go to your class accounts and execute the following command:
getStarterFinal
This will create the directory Final. You will find all the files you need there. We will explain the purpose of each file in the next section.
To compile your program, the following command creates the executable called run:
gcc -std=c99 final.c bship.o -o run
Depending on where you are developing your code (e.g., on the class accounts), you can also use the following, shorter command instead. It essentially is an alias for the full command.
make
You may develop your code on any platform. However, you need to make sure that it compiles and runs properly on the Linux servers. If it does not compile or run there, you will not receive any credit. Unlike the HW assignments, we are not strict on the formatting of the output. There are therefore no test scripts. However, there is a demo version of the program so you can see what a working version looks like. It also helps you answer questions like "what happens if "; you can just try it out (so please, do this first before asking us).
Header File: bship.h
We have already implemented several functions for you and put them in a library. You can find the function declarations in the header file bship.h, together with a description of what each function does. You will call these functions in the program you are asked to create. We will refer to these functions as needed when detailing the deliverables for your program. The function implementations themselves are hidden from you; they are in the bship.o file, which is also part of your starter code but it is in a binary (i.e., non-readable) format.
In that same bship.h header file, you also find the following constants:
When grading, we may change these constants (but always keep ROWS and COLUMNS >0 and <30 and FLEETSIZE >0 and <7). You may change these constants for testing purposes as well. However, do not change any other parts of the bship.h header file.
Finally, in the header file, there is also the definition of the struct Ship. This data structure contains the following information for each ship:
Main C file:final.c
All the code you write must go in the file final.c. It already contains all the header files you need. You cannot include any additional header files or libraries. You can (and should) write additional functions. You cannot use global variables (there is a 40 point penalty if you do use global variables).
The code in main() already declares an array to hold all the ships you need to place. It also calls our function initialize_ships() to initialize that array. You should not modify these two lines of code. The function initialize_ships() reads the ship info from the file ships.txt. You can view/modify this file in your text editor. It has a line for each ship, containing the length followed by the name. You can modify this file for testing purposes (we will change this file for grading). Make sure that the constant FLEETSIZE matches the number of ships in this file (so if you add more ships, you should modify FLEETSIZE in bhips.h accordingly).
Demo:final_demo
This executable is a demo of what a completed final should look like. It includes one extra functionality, showing the current state of the board, that you are not explicitly asked to implement. However, while optional, it is highly encouraged that you consider implementing something similar to help you with debugging (but it is not graded). If you do, it is fine if the code you submit also shows the board (similar to what we do in our demo).
To execute the demo program, type the command:
./final_demo
In this section, we describe the various functionalities you are asked to implement, as well as how many points they are worth. Make sure that no matter how much you were able to implement, your code compiles. If it does not compile, you will not pass the exam.We highly recommend making regular copies of your code and submitting intermediate working versions (i.e., versions that compile).
For each of the functionalities, the points are assigned based on correct execution. However, we may assign partial credit by looking at your code as well. Keep in mind that if your code is not organized clearly, we will not be able to understand it (and thus give partial credit). As such, writing easy to read code with proper comments is important.
Note that when describing the functionalities to implement, two of them are labeled as "[core]". We need both of them to work correctly in order to test and grade your code. These core functionalities, while stripped down to the bare minimum, are thus crucial and all others build on top of them. For other parts of the assignment, you can still implement later functionalities even if you didn't complete earlier ones.
Read through the entire assignment first before starting to code. Knowing what all the functionalities are will impact what data structures you use and how you organize your code. For example, we don't tell you how to represent the board or keep track of any of the information. That is something you can decide. Take some time to plan your approach before you start!
Part a: Placing the First Ship
Ask the user where to place a single ship on the board by calling the function ask_input(). If the ship cannot be placed, you should call the function msg_place_failure(). This happens when any part of the ship would fall outside of the board. Otherwise, you should call the function msg_place_success().
You need to implement this functionality to proceed to the remainder of the exam. Together with functionality 2a, it allows us to have a foundation for testing. However, if you are unable to do this, you can instead place a ship of type 1 at coordinates (0,0) in the vertical position and print the following message to screen "Unable to implement 1a. Using default placement instead.\n".
Note that part b will expand on part a. When you implement part b, you will have automatically implemented part a as well, so you may want to consider both together. We have split them here because part a is a core functionality. Also consider combining with part c.
The functions ask_input(), msg_place_failure() and msg_place_success() are part of the library we created for you. Their description in bship.h tells you how to call them.
The function ask_input() provides the type (a number between 1 and FLEETSIZE), the orientation (the letter 'V' for vertical or H for horizontal) and the (x,y) coordinates of the bottom left corner of the ship that the user wants to place (see also footnote ). For the example on page 2, the green ship corresponds to type 1 (using the example ships.txt), orientation H and coordinates x equal to 8 and y equal to 1. The (x,y) for the other two ships are (5,2) and (1,6). The function ask_input() will return valid values for the ship type and orientation, but the x and y coordinates can be any integer. If the user enters * instead of any of the requested inputs, the function returns 0. In that case, your code should not call msg_place_failure() or msg_place_success() but simply proceed with the rest of your program.
Part b: Placing Additional Ships
Place multiple ships on the board, again with the function ask_input(). Each time, call the functions msg_place_failure() and msg_place_success() accordingly. Note that placement also fails if ships overlap. Repeat until all FLEETSIZE ships have been placed. Each If the user entered * in response to any of the questions of ask_input(), the process finishes and the program should proceed to the next functionalities. In this case, only a subset of the ships would have been placed on the board; all other upcoming tasks should still be supported as normal, just with fewer total ships on the board.
Part c: Using a Function
Place all of functionality 1 inside a function by itself (separate from the other functionalities), which is called from within main(). This function can of course in turn call other functions. Basically, the idea is to use a separate function for your placing-the-ships functionality.
Part a: Individual Hits
Use the ask_shot() function to ask the user for (x,y) coordinates to shoot at, separated by a comma. If the user enters * instead, the function returns 0; otherwise it returns 1. Only call ask_shot() if at least one ship was placed successfully (as part of functionality 1).
When a shot hits one of the ships on the board in a spot where it had not been hit before, call the function msg_hit_success(). Otherwise (which includes a shot being outside of the board), call the function msg_hit_failure(). Do this repeatedly until the user enters *, at which point the program should proceed to the next steps.
Part b: Destroying a Ship
As part of the functionality of part a, whenever a ship gets completely destroyed (the last hit caused it to have been hit now in all its positions), call the function msg_ship_destroyed(). Pass the name of the ship that has been destroyed as the argument to this function. A ship can only be destroyed once.
When all ships have been destroyed, stop calling ask_shot() and the program should proceed to the next steps. This therefore should happen when either the user has entered * or all ships have been destroyed.
Part c: Using a Function
Place all of functionality 2 inside a function by itself (separate from the other functionalities), which is called from within main(). This function can of course in turn call other functions. Basically, the idea is to use a separate function for your detecting-hits functionality.
Part a: Reporting the Score
At the end of the program, report how many total shots the user attempted (combination of successful and unsuccessful ones) by writing this value to a file named score.txt. This file should only contain this value (and nothing else).
Part b: Individual Hits
At the end of the program, call the function msg_end(). As a function argument, pass it a string stored in a dynamic array. It needs to be a dynamic array; otherwise the function msg_end() will have a run-time error since it frees the dynamic memory inside this function. The content of the string should be "The last ship you sunk was of type: ", followed by the name of the last ship that was destroyed (the maximum length of the ship name string is 20). If no ship was destroyed yet, the function msg_end() should not be called. Remember that you cannot include any additional libraries.
Part a: Implementing the Function
We gave you the function ask_shot(). Now we ask you to implement that functionality yourself and name the function ask_shot2(). You must use the same function prototype (i.e., the exact same function parameters) as ask_shot().
You may assume that the user enters data of the correct type and formatting, i.e., two integers separated by a comma, or *. You therefore do not need to implement the functionality where you ask the user repeatedly if the format doesn't match. Your function is a simplified version of our ask_shot(). The input question should be somewhat similar to ours in ask_input() but we are not strict here (i.e., your formatting does not need to match exactly; there is no test script).
If you implement ask_shot2(), you must use it in your code. This means you may need to modify your functionality 2 to use ask_shot2() instead of ask_shot(). If you did not implement ask_shot2(), you can just keep on using ask_shot() in your functionality 2.