battleship 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) <2012> <David Rodriguez>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,7 @@
1
+ battleship
2
+ ===============
3
+
4
+ Version of the classic board game Battleship written in Ruby.
5
+
6
+ When placing ships, "starting position" refers to the top end of the ship when
7
+ placing vertically or left end of the ship when placing horizontally.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/battleship/game'
4
+
5
+ Game.new.play
@@ -0,0 +1,69 @@
1
+ require_relative 'grid_cell'
2
+ require_relative 'ship'
3
+
4
+ class Board
5
+
6
+ attr_accessor :grid
7
+
8
+ NUM_SHIPS = 5
9
+ BOARD_DIM = 10
10
+ ROW = ['A','B','C','D','E','F','G','H','I','J']
11
+ COLUMN = ['1','2','3','4','5','6','7','8','9','10']
12
+
13
+ def initialize
14
+ @grid = Array.new(BOARD_DIM).map! {Array.new(BOARD_DIM).map! {Grid_Cell.new}}
15
+ end
16
+
17
+ #print board grid
18
+ def to_s
19
+ row_letter = ('A'..'Z').to_a
20
+ i = 0
21
+ puts " 1 2 3 4 5 6 7 8 9 10"
22
+ @grid.each do |row|
23
+ print row_letter[i] + ' '
24
+ row.each {|cell| print cell.to_s + ' '}
25
+ print "\n"
26
+ i += 1
27
+ end
28
+ end
29
+
30
+ #validate ship placement remains within board
31
+ def valid_coordinates?(ship, start_position, orientation)
32
+ if orientation == :horizontal
33
+ (start_position[:column] + Ship::LENGTH[ship]) <= BOARD_DIM
34
+ else
35
+ (start_position[:row] + Ship::LENGTH[ship]) <= BOARD_DIM
36
+ end
37
+ end
38
+
39
+ #validate ship placement conflicts with another placed ship
40
+ def check_clearance?(ship, start_position, orientation)
41
+ length = Ship::LENGTH[ship]
42
+ row = start_position[:row]
43
+ column = start_position[:column]
44
+ length.times do
45
+ if self.grid[row][column].status != :open
46
+ return false
47
+ elsif orientation == :horizontal
48
+ column += 1
49
+ else
50
+ row += 1
51
+ end
52
+ end
53
+ return true
54
+ end
55
+
56
+ # method currently not used
57
+ # def hit_or_miss(cell)
58
+ # if @grid[cell[:row], cell[:column]].status == :open
59
+ # return :miss
60
+ # elsif @grid[cell[:row], cell[:column]].status == :hit ||
61
+ # @grid[cell[:row], cell[:column]].status == :miss
62
+ # return :called
63
+ # else
64
+ # @grid[cell[:row], cell[:column]].status = :hit
65
+ # return :hit
66
+ # end
67
+ # end
68
+
69
+ end
@@ -0,0 +1,269 @@
1
+ require_relative 'player'
2
+ require_relative 'board'
3
+
4
+ class Game
5
+
6
+ attr_reader :opp_targeting_queue
7
+
8
+ def play
9
+ puts "\nLet's play some Battleship!\n\n"
10
+ set_player
11
+ set_opponent
12
+ puts "\nNow place your ships, #{@player.name}."
13
+ deploy_ship(@player, @player.carrier)
14
+ deploy_ship(@player, @player.battleship)
15
+ deploy_ship(@player, @player.destroyer)
16
+ deploy_ship(@player, @player.submarine)
17
+ deploy_ship(@player, @player.patrol)
18
+ deploy_opp_ships
19
+ play_rounds
20
+ end
21
+
22
+ def set_player
23
+ name = ''
24
+ while name.empty? do
25
+ print "Enter name: "
26
+ name = gets.chomp.rstrip
27
+ @player = Player.new(name)
28
+ end
29
+ end
30
+
31
+ def set_opponent
32
+ @opponent = Player.new('Opponent')
33
+ @opp_targeting_queue = []
34
+ Board::ROW.each do |row|
35
+ Board::COLUMN.each do |column|
36
+ @opp_targeting_queue << [row, column]
37
+ end
38
+ end
39
+ @opp_targeting_queue.shuffle! #queue of random ooponent shot coordinates
40
+ end
41
+
42
+ def deploy_ship(player, ship)
43
+
44
+ #prompt for placement orientation and validate input
45
+ valid = false
46
+ while valid == false do
47
+ print "\n"
48
+ player.board.to_s #print board for reference
49
+ position = {}
50
+ while valid == false do
51
+ print "\n#{ship.type.capitalize} orientation: horizontal(H) or vertical(V)? "
52
+ input = gets.chomp.rstrip.upcase
53
+ if input == 'H' || input == 'HORIZONTAL'
54
+ orientation = :horizontal
55
+ valid = true
56
+ elsif input == 'V' || input == 'VERTICAL'
57
+ orientation = :vertical
58
+ valid = true
59
+ else
60
+ puts "Invalid orientation entry."
61
+ end
62
+ end
63
+
64
+ #prompt for placement starting position and validate input
65
+ valid = false
66
+ while valid == false do
67
+ print "\n#{ship.type.capitalize} starting position (Ex. A10): "
68
+ input = gets.chomp.rstrip.upcase
69
+ position[:row] = Board::ROW.rindex(input.split(//, 2)[0])
70
+ position[:column] = Board::COLUMN.rindex(input.split(//, 2)[1])
71
+ if !position[:row].nil? && !position[:column].nil?
72
+ valid = true
73
+ else
74
+ puts "Invalid coordinates."
75
+ end
76
+ end
77
+
78
+ #validate board clearances for given orientation and position
79
+ valid = false
80
+ if player.board.valid_coordinates?(ship.type, position, orientation) &&
81
+ player.board.check_clearance?(ship.type, position, orientation)
82
+ ship.place_ship(player.board, position, orientation)
83
+ valid = true
84
+ else
85
+ puts "Invalid position for ship."
86
+ end
87
+ end
88
+ end
89
+
90
+ #randomly place opponent's ships
91
+ def deploy_opp_ships
92
+ position = {}
93
+ Ship::LENGTH.keys.each do |ship|
94
+ valid = false
95
+ while valid == false
96
+ orientation = [:horizontal, :vertical].sample
97
+ if orientation == :horizontal
98
+ rows = Board::ROW
99
+ columns = Board::COLUMN[0..9 - ship.length]
100
+ else
101
+ rows = Board::ROW[0..9 - ship.length]
102
+ columns = Board::COLUMN
103
+ end
104
+
105
+ position[:row] = Board::ROW.rindex(rows.sample)
106
+ position[:column] = Board::COLUMN.rindex(columns.sample)
107
+
108
+ if @opponent.board.valid_coordinates?(ship, position, orientation) &&
109
+ @opponent.board.check_clearance?(ship, position, orientation)
110
+ valid = true
111
+ case ship
112
+ when :carrier
113
+ @opponent.carrier.place_ship(@opponent.board, position, orientation)
114
+ when :battleship
115
+ @opponent.battleship.place_ship(@opponent.board, position, orientation)
116
+ when :destroyer
117
+ @opponent.destroyer.place_ship(@opponent.board, position, orientation)
118
+ when :submarine
119
+ @opponent.submarine.place_ship(@opponent.board, position, orientation)
120
+ when :patrol
121
+ @opponent.patrol.place_ship(@opponent.board, position, orientation)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ # @opponent.board.to_s #print for testing only
127
+ end
128
+
129
+ #alternate rounds between player and opponent, determine if game won
130
+ def play_rounds
131
+ puts "\n\nTime to sink some ships! Good luck, #{@player.name}\n\n"
132
+ game_over = false
133
+ while game_over == false
134
+ @player.print_boards
135
+ player_round
136
+ if @opponent.ships_left == 0
137
+ winner = @player.name
138
+ game_over = true
139
+ next
140
+ end
141
+
142
+ opponent_round
143
+ if @player.ships_left == 0
144
+ winner = "Opponent"
145
+ game_over = true
146
+ end
147
+ end
148
+ puts "\n\n#{winner} WINS!\n\n"
149
+ end
150
+
151
+ #prompt player for shot coordinates, validate, assess hit or miss
152
+ def player_round
153
+ target = {}
154
+ valid = false
155
+ while valid == false
156
+ print "\nYour turn. Target coordinates: "
157
+ input = gets.chomp.rstrip.upcase
158
+ target[:row] = Board::ROW.rindex(input.split(//, 2)[0])
159
+ target[:column] = Board::COLUMN.rindex(input.split(//, 2)[1])
160
+ if !target[:row].nil? && !target[:column].nil?
161
+ valid = true
162
+ else
163
+ puts "Invalid coordinates."
164
+ end
165
+ end
166
+
167
+ if @opponent.board.grid[target[:row]][target[:column]].status == :open
168
+ puts "\n\"MISS!\""
169
+ @player.target_board.grid[target[:row]][target[:column]].miss
170
+ else
171
+ puts "\n\"HIT!\""
172
+ @player.target_board.grid[target[:row]][target[:column]].hit
173
+ case @opponent.board.grid[target[:row]][target[:column]].status
174
+ when :carrier
175
+ @opponent.carrier.hit
176
+ if @opponent.carrier.sunk?
177
+ @opponent.ships_left -= 1
178
+ puts "Opponent's carrier sunk! #{@opponent.ships_left} more ships to go."
179
+ end
180
+ when :battleship
181
+ @opponent.battleship.hit
182
+ if @opponent.battleship.sunk?
183
+ @opponent.ships_left -= 1
184
+ puts "Opponent's battleship sunk! #{@opponent.ships_left} more ships to go."
185
+ end
186
+ when :destroyer
187
+ @opponent.destroyer.hit
188
+ if @opponent.destroyer.sunk?
189
+ @opponent.ships_left -= 1
190
+ puts "Opponent's destroyer sunk! #{@opponent.ships_left} more ships to go."
191
+ end
192
+ when :submarine
193
+ @opponent.submarine.hit
194
+ if @opponent.submarine.sunk?
195
+ @opponent.ships_left -= 1
196
+ puts "Opponent's submarine sunk! #{@opponent.ships_left} more ships to go."
197
+ end
198
+ when :patrol
199
+ @opponent.patrol.hit
200
+ if @opponent.patrol.sunk?
201
+ @opponent.ships_left -= 1
202
+ puts "Opponent's patrol boat sunk! #{@opponent.ships_left} more ships to go."
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ #opponent takes shot from queue, assess hit or miss
209
+ def opponent_round
210
+ puts "\n---------- Opponent's turn ----------"
211
+ target_coords = opp_targeting_queue.pop
212
+ target = {}
213
+
214
+ target[:row] = Board::ROW.rindex(target_coords[0])
215
+ target[:column] = Board::COLUMN.rindex(target_coords[1])
216
+
217
+ print "\nOpponent called \"#{target_coords[0]}#{target_coords[1]}\" "
218
+ if @player.board.grid[target[:row]][target[:column]].status == :open
219
+ puts "- MISS!\n\n"
220
+ else
221
+ puts "- HIT!"
222
+ case @player.board.grid[target[:row]][target[:column]].status
223
+ when :carrier
224
+ @player.carrier.hit
225
+ if @player.carrier.sunk?
226
+ @player.ships_left -= 1
227
+ puts "\nYour carrier has been sunk!"
228
+ else
229
+ puts "\nYour carrier has been hit!"
230
+ end
231
+ when :battleship
232
+ @player.battleship.hit
233
+ if @player.battleship.sunk?
234
+ @player.ships_left -= 1
235
+ puts "\nYour battleship has been sunk!"
236
+ else
237
+ puts "\nYour battleship has been hit!"
238
+ end
239
+ when :destroyer
240
+ @player.destroyer.hit
241
+ if @player.destroyer.sunk?
242
+ @player.ships_left -= 1
243
+ puts "\nYour destroyer has been sunk!"
244
+ else
245
+ puts "\nYour destroyer has been hit!"
246
+ end
247
+ when :submarine
248
+ @player.submarine.hit
249
+ if @player.submarine.sunk?
250
+ @player.ships_left -= 1
251
+ puts "\nYour submarine has been sunk!"
252
+ else
253
+ puts "\nYour submarine has been hit!"
254
+ end
255
+ when :patrol
256
+ @player.patrol.hit
257
+ if @player.patrol.sunk?
258
+ @player.ships_left -= 1
259
+ puts "\nYour patrol boat has been sunk!"
260
+ else
261
+ puts "\nYour patrol boat has been hit!"
262
+ end
263
+ end
264
+ @player.board.grid[target[:row]][target[:column]].hit
265
+ end
266
+ puts "-------------------------------------\n\n"
267
+ end
268
+
269
+ end
@@ -0,0 +1,31 @@
1
+ class Grid_Cell
2
+
3
+ attr_accessor :status
4
+
5
+ #cell fill characters
6
+ FILL_CHAR = {:open => '+',
7
+ :hit => 'X',
8
+ :miss => '0',
9
+ :carrier => 'C',
10
+ :battleship => 'B',
11
+ :destroyer => 'D',
12
+ :submarine => 'S',
13
+ :patrol => 'P'}
14
+
15
+ def initialize(status = :open)
16
+ @status = status
17
+ end
18
+
19
+ def to_s
20
+ FILL_CHAR[@status]
21
+ end
22
+
23
+ def hit
24
+ @status = :hit
25
+ end
26
+
27
+ def miss
28
+ @status = :miss
29
+ end
30
+
31
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'board'
2
+ require_relative 'ship'
3
+
4
+ class Player
5
+
6
+ attr_accessor :name, :ships_left
7
+ attr_reader :board, :target_board, :carrier, :battleship, :destroyer, :submarine, :patrol
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ @board = Board.new
12
+ @target_board = Board.new
13
+ @carrier = Ship.new(:carrier)
14
+ @battleship = Ship.new(:battleship)
15
+ @destroyer = Ship.new(:destroyer)
16
+ @submarine = Ship.new(:submarine)
17
+ @patrol = Ship.new(:patrol)
18
+ @ships_left = Board::NUM_SHIPS
19
+ end
20
+
21
+ def to_s
22
+ @name
23
+ end
24
+
25
+ #print side-by-side ship status and shpts taken boards
26
+ def print_boards
27
+ puts " SHIP STATUS SHOTS TAKEN"
28
+ puts " 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10"
29
+ row_letter = ('A'..'Z').to_a
30
+ row_number = 0
31
+ @board.grid.each do |row1|
32
+ print row_letter[row_number] + ' '
33
+ row1.each {|cell| print cell.to_s + ' '}
34
+ print " "
35
+ print row_letter[row_number] + ' '
36
+ @target_board.grid[row_number].each {|cell| print cell.to_s + ' '}
37
+ print "\n"
38
+ row_number += 1
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,43 @@
1
+ class Ship
2
+
3
+ LENGTH = {:carrier => 5,
4
+ :battleship => 4,
5
+ :destroyer => 3,
6
+ :submarine => 3,
7
+ :patrol => 2}
8
+
9
+ attr_accessor :type, :length, :hits
10
+
11
+ def initialize(type)
12
+ @type = type
13
+ @length = LENGTH[type]
14
+ @hits = 0
15
+ end
16
+
17
+ def place_ship(board, start_position, orientation)
18
+ row = start_position[:row]
19
+ column = start_position[:column]
20
+ @length.times do
21
+ if orientation == :horizontal
22
+ board.grid[row][column].status = @type
23
+ column += 1
24
+ else
25
+ board.grid[row][column].status = @type
26
+ row += 1
27
+ end
28
+ end
29
+ end
30
+
31
+ def sunk?
32
+ @length == @hits
33
+ end
34
+
35
+ def hit
36
+ @hits += 1
37
+ end
38
+
39
+ def to_s
40
+ "#{@board}'s #{@type}"
41
+ end
42
+
43
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: battleship
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Rodriguez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'battleship
15
+
16
+ ===============
17
+
18
+
19
+ Version of the classic board game Battleship written in Ruby.
20
+
21
+
22
+ When placing ships, "starting position" refers to the top end of the ship when
23
+
24
+ placing vertically or left end of the ship when placing horizontally.'
25
+ email: davidrodriguez212@gmail.com
26
+ executables:
27
+ - battleship
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - bin/battleship
32
+ - lib/battleship/board.rb
33
+ - lib/battleship/game.rb
34
+ - lib/battleship/grid_cell.rb
35
+ - lib/battleship/player.rb
36
+ - lib/battleship/ship.rb
37
+ - LICENSE
38
+ - README
39
+ homepage:
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '1.9'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.25
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Ruby text based version of classic board game
63
+ test_files: []