Overview

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.

Logistics

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).

Starter Code

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:

  • ROWS and COLUMNS they specify the dimensions of the board (for the example on page 2, ROWS was 7 and COLUMNS was 10)
  • FLEETSIZEthis gives the total number of ships (for the example on page 2, FLEETSIZE was 3)

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:

  • lengththe length of the ship
  • namea string with the name of the 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

What to Implement

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!

Functionality 1:Placing the Ships

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.

Functionality 2: Detecting Hits

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.

Functionality 3: End of the Game

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.

Functionality 4: Asking for User Input

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.

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.