Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Battleship project (pair programming with John B) #6

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4744894
Test push
jmbuscemi May 11, 2015
cc9c84b
Delete test_push.md
jmbuscemi May 11, 2015
62e4a29
Initial Commit
jmbuscemi May 11, 2015
fecbe5e
Merge branch 'master' of https://github.com/jmbuscemi/battleship
jmbuscemi May 11, 2015
bb29817
Ship place accross/down. Ship can't be placed twice
jmbuscemi May 11, 2015
3f9281e
Add overlap ships
jmbuscemi May 11, 2015
9adca59
ship can be hit
aaronwiggins May 11, 2015
a72ff68
unplaced ship not sunk
aaronwiggins May 11, 2015
9e86c41
grid class added. add empty grid
aaronwiggins May 11, 2015
ee02bc0
place ship on grid
aaronwiggins May 12, 2015
87a38ef
Add require ./grid
jmbuscemi May 12, 2015
a234eec
Remove require ./grid
jmbuscemi May 12, 2015
ee4402b
Add misses array. Change how coverage array is made.
jmbuscemi May 12, 2015
5f44760
Run test 19-31
jmbuscemi May 12, 2015
e5b36e8
Can't place overlapping ships
jmbuscemi May 12, 2015
05745a4
fix typo
jmbuscemi May 12, 2015
e4ba947
Can't place overlapping ships.
jmbuscemi May 12, 2015
bbb7cc8
No misses outside grid.
jmbuscemi May 12, 2015
6ccbd54
Fix typos...again.
jmbuscemi May 12, 2015
a989b27
Remove unnecessary code
jmbuscemi May 12, 2015
c61ca3f
Create grid as array. Grid display will show placed ships
jmbuscemi May 12, 2015
96a39d9
Add ignore files
jmbuscemi May 12, 2015
2fd9dcd
Add repeat hit
jmbuscemi May 13, 2015
966256a
sunk? method on ship.rb made more concise
aaronwiggins May 13, 2015
5d19fb0
Update ship.sunk method. Clean up code
jmbuscemi May 13, 2015
427f037
Clean up code with enumerable methods. Create used grid. Grid can't b…
jmbuscemi May 13, 2015
b5bb866
Add x_of and y_of methods.
jmbuscemi May 13, 2015
5d1739b
Players and Computers have grids
jmbuscemi May 13, 2015
5eb0dec
Create ships.
jmbuscemi May 13, 2015
27558ba
Human player can place ships
aaronwiggins May 13, 2015
f32f272
human player cannot overlap ships
aaronwiggins May 14, 2015
3f885f9
Computer player automatically place ships
aaronwiggins May 14, 2015
582106d
Pass more tests. All the way to 34...that was tough
jmbuscemi May 14, 2015
789503b
Place ships with no overlapping ships.
jmbuscemi May 14, 2015
f9f1dbe
Automatically place ships...computer is smart
jmbuscemi May 14, 2015
0a16c39
Final commit...for now
jmbuscemi May 14, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_store
sample_grid.rb
fire_at.rb
69 changes: 10 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,15 @@
# Battleship
# Battle(?)ship

## Description

Write a program that plays the game of Battleship. In the first version, the user can act as Player 1, and the computer can act as Player 2.
This program is a little game called Battleship. Well that's what it could be, but we haven't passed all of the tests in our test suite. And thus, we don't have a working game.

## Objectives
We did however, pass 34 of the 41 tests. That's not terribly shabby. Leave us alone, we're new to this.
[Relevant XKCD](http://xkcd.com/1319/)

After completing this assignment, you should...

* Understand the basics of control flow, variables, and methods in Ruby.
* Understand the basics of Object Oriented Programming.
* Understand the basic use cases of Git for single-developer projects.
* Understand the purpose of test suites and how they can assist in development.
* Understand how frustrating it would have been to try to check and debug this assignment without written tests!
* Be able to build a game-playing application that asks the user for input and provides output to the user.
* Be able to create a computer player that can act like a human (albeit not necessarily an intelligent one).
* Be able to write an program that can satisfy a test suite.

## Deliverables

* **A Repository.** Fork this repository to your own github account.
* **A README.** Wipe out this README and write your own. It should tell readers how to play your version of the game.
* **Ruby Files.** You'll need a lot of these, and you'll be creating them from scratch. You should have one per class, plus at least one other to be executed from the command line to run the game.
* **A Playable Game.** When I clone your repository, I should be able to run your program and play through an entire game of Battleship. I should be able to win and to lose.

When you are finished, create a pull request.


## Normal Mode

Your task for this project, whether or not you choose to accept it, is to write a program that plays the game of Battleship. The user can act as Player 1, and the computer can act as Player 2. However, as you work through the assignment, you'll notice that you're also building it so that two humans could play each other (or two computer players, but that's not as much fun).

The rules of the game are given in the [Hasbro instructions](http://www.hasbro.com/common/instruct/battleship.pdf).

The major requirement of this project is for the TEST SUITE TO PASS. You are coding to the tests, and ideally, you're making them pass one at a time. As discussed in class, I would suggest making the first test pass USING THE SIMPLEST MEANS POSSIBLE. Then move on to the second test, the third test and so on. You should commit after each test passes.

Just to be clear, your computer player does not have to be smart. It just has to play until the game is finished.

Here's the unfortunately odd thing. READ THIS. If you're ever about to call `gets.chomp` in your code to prompt the user for his/her input, don't. Put the following code at the top of any file that needs to prompt the user:

```
def get_user_input
gets.chomp
end
```

This goes BEFORE you start defining your class. I know that looks stupid, and I apologize for it, but the test suite will only run if you ALWAYS ask for user input by calling `get_user_input` rather than `gets.chomp`. Sorry again.

## Hard Mode

This game currently only works for 9x9 grids, and for five ships with pre-set lengths. Add two optional parameters to `Grid.new` to allow the user to specify width and height of the board. Add one optional parameter to `Game.new` which is an array of integers. These integers define the lengths of the ships to be placed.

In addition to the above, add ten more (meaningful) tests to the test suite. Get them to pass!

## Nightmare Mode

You have two paths to choose from in Nightmare mode:

1. In addition to the above, modify your existing code to ask the user which type of game he/she would like to play: regular or SALVO. Allow the user to select and play either game.

2. Modify your computer player so that it makes intelligent moves. In other words, Normal Mode is satisfied if the computer fires randomly. Hard mode required the computer to fire with some meaning. It has to have a chance of winning.

Don't forget to modify the test suite to continue to pass!
##Somethings we've learned
* The basics of control flow, variables, and methods in Ruby.
* The basics of Object Oriented Programming.
* The basic use cases of Git for single-developer projects.
* We collaborated on a forked project, and never had any real issues of "Hey! Where'd that file go?"
* Using a test suite is pretty nice, because otherwise this would have been way over our heads! Oh and DEBUGGING WOULD HAVE BEEN A NIGHTMARE MODE ALL ON IT'S OWN!!!
6 changes: 6 additions & 0 deletions battleship.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#create battleship.rb
require './player'
require './human_player'
require './computer_player'
require './ship'
require './grid'
4 changes: 2 additions & 2 deletions battleship_test.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
require 'minitest/autorun'
require 'minitest/pride'

require 'byebug'
#Note: This line is going to fail first.
require './battleship.rb'
require './battleship'

$mock_inputs = []
def get_user_input
Expand Down
40 changes: 40 additions & 0 deletions computer_player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class ComputerPlayer < Player
attr_reader :name, :ships

def initialize
@name = "HAL 9000"
super()
@ships = []
end

def place_ships(array)
array.each do |i|
placed = false
until placed == true
ship = Ship.new(i)
orientation = [true,false].sample
if orientation == "Across"
orientation = true
x = (1..10-i).to_a.sample
y = i
else
orientation = false
x = i
y = (1..10-i).to_a.sample
end
ship.place(x, y, orientation)
if ship.coverage.any? {|x| grid.has_ship_on?(x[0],x[1])}
else
@grid.place_ship(ship,x,y, orientation)
@ships << ship
placed = true
end
end
end
puts "HAL 9000 has placed its ships.\n"
end

def call_shot
computer_shot = ("A".."J").to_a.sample + (1..10).to_a.sample.to_s
end
end
95 changes: 95 additions & 0 deletions grid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
class Grid
#include FireAt
attr_reader :ships

def initialize
@ships = []
@hits = []
@misses = [] #maybe make this shots and ALL shots get put in here
end

def has_ship_on?(x_loc, y_loc)
@ships.each do |s|
return s if s.covers?(x_loc, y_loc)
end
false
end

def display_header
puts " 1 2 3 4 5 6 7 8 9 10"
puts " -----------------------------------------"
end

def display_footer
puts " -----------------------------------------"
end

def display
letters = ["A","B","C","D","E","F","G","H","I","J"]
display_header
(1..10).each do |row|
output_row = "#{letters[row - 1]} |"
(1..10).each do |column|
if @hits.include?([column, row])
output_row += " X |"
elsif has_ship_on?(column, row)
output_row += " O |"
else
output_row += " |"
end
end
puts output_row
end
display_footer
end

def place_ship(ship, x_loc, y_loc, orientation)
ship.place(x_loc, y_loc, orientation)
if @ships.any? {|s| s.overlaps_with?(ship)}

else
@ships << ship
true
end

end

def fire_at(x_loc, y_loc)
ship_that_was_hit = has_ship_on?(x_loc, y_loc)
if @ships.empty? || x_loc > 10 || y_loc > 10
false
elsif ship_that_was_hit
if @hits.include?([x_loc, y_loc])
false
else
@hits << [x_loc, y_loc]
ship_that_was_hit.fire_at(x_loc, y_loc)
true
end
else
@misses << [x_loc, y_loc]
false
end
end

#Add this .all? method
def sunk?
return false if @ships.empty?
@ships.all? {|s| s.sunk?}

#if @ships != [] && @hits == @ships
#end
end

def x_of(guess)
guess[1..-1].to_i
end

def y_of(guess)
convert_hash = {1 => :A, 2 => :B, 3 => :C, 4 => :D, 5 => :E, 6 => :F,
7 => :G, 8 => :H, 9 => :I, 10 => :J}.invert
guess = convert_hash[guess[0].to_sym]
end


end
46 changes: 46 additions & 0 deletions human_player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
def get_user_input
gets.chomp
end

class HumanPlayer < Player
attr_reader :name, :ships

def initialize(name = "Dave")
@name = name
super()
@ships = []

end

def place_ships(array)
array.each do |i|
placed = false
until placed == true
ship = Ship.new(i)
puts "#{@name}, where would you like to place a ship of length #{i}?"
coordinate = get_user_input
puts "Across or Down?"
orientation = get_user_input
if orientation == "Across"
orientation = true
else
orientation = false
end
ship.place(@grid.x_of(coordinate), @grid.y_of(coordinate), orientation)
if ship.coverage.any? {|x| grid.has_ship_on?(x[0],x[1])}
puts "Unfortunately, that ship overlaps with one of your other ships. Please try again.\n"
else
@grid.place_ship(ship,@grid.x_of(coordinate), @grid.y_of(coordinate), orientation)
@ships << ship
placed = true

end
end
end
end

def call_shot
puts "Dave, please enter the coordinates for your next shot (e.g. 'B10'):\n"
shot = get_user_input
end
end
9 changes: 9 additions & 0 deletions player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Player
attr_accessor :grid

def initialize
@grid = Grid.new
end


end
66 changes: 66 additions & 0 deletions ship.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
class ShipAlreadyPlacedError < StandardError
end

class Ship
#include FireAt
attr_reader :length, :coverage

def initialize (length)
@length = length
@coverage = []
@hits = []
@misses = []
end

def place(x_loc, y_loc, orientation)
if @x_start.nil?
@x_start = x_loc
@y_start = y_loc
if orientation
(x_loc..(x_loc + @length - 1)).each {|i| @coverage << [i, y_loc]}
else
(y_loc..(y_loc + @length - 1)).each {|i| @coverage << [x_loc, i]}
end
else
false
end
end

def covers?(x_loc, y_loc)
if @coverage.include?([x_loc, y_loc])
true
else
false
end
end

def overlaps_with?(other)
@coverage.each do |item|
x = item[0]
y = item[1]
if other.covers?(x, y)
return true
end
end
false
end

def fire_at(x_loc, y_loc)
if @coverage.include?([x_loc, y_loc])
@hits << [x_loc, y_loc]
elsif @coverage.empty?
false
elsif !@coverage.include?([x_loc, y_loc])
@misses << [x_loc, y_loc]
false
end
end

def sunk?
if @coverage != [] && @hits == @coverage
true
end
end


end