| The game Battleship is a guessing game played by two people. The game is played on a board of square grids, normally 10x10. Each ship occupies a number of consecutive squares on the board, arranged either horizontally or vertically, but not overlapping each other. The lengths of the ships are given before the game, for example:
- one ship of length 5
- two ships of length 4
- one ship of length 3
In the game, the two players take turns choosing a single square from the opponent's board, and shooting at it. The player whose ships are being shot at then announces whether any ship was hit. If all the grids that a single ship occupies have been hit, that ship is sunk. If all of a player's ships are sunk, that player loses.
In this problem we consider a more advanced version of the game. During each turn you may shoot at multiple squares at the same time. As feedback, you will be given the number of shots that hit each ship, but not told which shots they were. Your goal is to sink all the ships as soon as possible. (You are only playing as an attacker -- you don't have to place ships).
You should write a method play that takes a int size, a int[] ships, and a int fireNum. size gives the length and width of the board. ships gives the sizes of the ships (one per element). fireNum gives the maximum number of shots that you may fire in one turn. For each set of shots, your method should call a static function fire in class Battleship which takes int[]s x and y giving the coordinates of your shots (x and y coordinates are indexed from 1). This function will return a int[] telling you how many of your shots hit each ship (elements of this return correspond to elements of ships). Once you have sunk every ship, you should return your favorite integer from your play method (this value will be ignored).
Failure to sink every ship will result in a score of 0.
Consider this example where size=10 and ships={5,4,4,3}.
During the first turn, you fire six shots: (4,2), (5,3), (6,4), (7,5), (6,6), and (5,6) (do this by calling fire({4,5,6,7,6,5},{2,3,4,5,6,6})).
The fire method would then return {1,2,0,1} meaning that you successfully hit 1, 2, 0, and 1 grids respectively for ship A, B, C and D.
You would then continue to fire more shots by making more calls to fire.
The scoring is based on the time it takes you to sink each ship. Suppose you sink the ship with index i at turn t[i] (your first set of shots are at turn 1), you will get a penalty of shipLength[i] * t[i]. Your total penalty for a test case will be the sum of the penalties for all the ships (this is what is reported as your score when you test the examples). Your final score for each test case will be based on how far your total penalty is from the lowest total penalty out of all competitors. For a particular test case, the best penalty will be found (call this best and your penalty pen). Your score will be the sum over all test cases of best/pen. So, if your penalty for a particular test case was 600, and the best penalty for that test case was 590, your score for that case would be 590/600.
The test cases are generated as follows: size will be chosen uniformly between 6 and 50, inclusive. fireNum will be chosen uniformly between 2 and size, inclusive. The number of ships will be equal to floor(size/2). Ships will be placed one at a time. First, an initial length for a ship is uniformly chosen between 2 and floor(size / 3 + 2), inclusive. Then the legal positionings (no conflict with the ships already placed) for that ship will be calculated. Whenever no legal positioning is found, the size will be decreased by one and the positionings will be recalculated. Afterwards, a random positioning will be chosen among all the legal ones.
|