battleships 0.1.0
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.
- checksums.yaml +7 -0
- data/lib/battleships/board.rb +101 -0
- data/lib/battleships/cell.rb +23 -0
- data/lib/battleships/coordinate_handler.rb +69 -0
- data/lib/battleships/game.rb +89 -0
- data/lib/battleships/player.rb +27 -0
- data/lib/battleships/ship.rb +45 -0
- data/lib/battleships.rb +8 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: da57b373b2bcf79e07fe79c339d158bcc1fdddae
|
4
|
+
data.tar.gz: 45a1287509da6d8faa344da78751d695a9cc6d40
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6f66402b6a19225f35cef3223ee606545aa01cc59613d4bc3d624ef6fb3caae303dd81e3625155ad831d4217be61d925e2522fb4bafd64d7871472e902a8497f
|
7
|
+
data.tar.gz: 5afa32f8b87a73c732263729fe29b2cfda58c5d5ecedef06c736c1374091eec90c396663190e890d0a181aa1142bde259dd4fe8d09029e3443fcf5b9a34afcd8
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative 'cell'
|
2
|
+
require_relative 'coordinate_handler'
|
3
|
+
|
4
|
+
class Battleships::Board
|
5
|
+
SIZE = 10
|
6
|
+
|
7
|
+
attr_accessor :width
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@grid = {}
|
11
|
+
@coord_handler = CoordinateHandler.new
|
12
|
+
@ships = []
|
13
|
+
initialize_grid
|
14
|
+
end
|
15
|
+
|
16
|
+
def place_ship ship, coordinate, orientation = :horizontally
|
17
|
+
coords = all_ship_coords ship, coordinate, orientation
|
18
|
+
|
19
|
+
coords.each { |coord| grid[coord].content = ship }
|
20
|
+
@ships << ship
|
21
|
+
end
|
22
|
+
|
23
|
+
def width
|
24
|
+
SIZE
|
25
|
+
end
|
26
|
+
|
27
|
+
def height
|
28
|
+
SIZE
|
29
|
+
end
|
30
|
+
|
31
|
+
def ships
|
32
|
+
# note we do not pass the source array here as it would enable
|
33
|
+
# callers to modify the board's ships, which would break encapsulation.
|
34
|
+
# Instead we return a duplicate.
|
35
|
+
@ships.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def receive_shot coordinate
|
39
|
+
coord_handler.validate coordinate
|
40
|
+
|
41
|
+
validate_coord_not_shot coordinate
|
42
|
+
|
43
|
+
cell = grid[coordinate]
|
44
|
+
cell.receive_shot
|
45
|
+
|
46
|
+
if cell.content
|
47
|
+
cell.content.sunk? ? :sunk : :hit
|
48
|
+
else
|
49
|
+
:miss
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def [] coordinate
|
54
|
+
coord_handler.validate coordinate
|
55
|
+
grid[coordinate]
|
56
|
+
end
|
57
|
+
|
58
|
+
def all_ships_sunk?
|
59
|
+
return false if ships.empty?
|
60
|
+
ships.all?(&:sunk?)
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :grid, :coord_handler
|
70
|
+
|
71
|
+
def initialize_grid
|
72
|
+
coord_handler.each do |coord|
|
73
|
+
grid[coord] = Cell.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def all_ship_coords ship, coord, orientation
|
78
|
+
coord_handler.validate coord
|
79
|
+
|
80
|
+
all_coords = coord_handler.from coord, ship.size, orientation
|
81
|
+
|
82
|
+
validate_all_ship_coords all_coords, ship.size
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_all_ship_coords coords, size
|
86
|
+
#ship is out of bounds if the ship is larger than the available coords
|
87
|
+
fail 'Out of bounds' if size > coords.length
|
88
|
+
|
89
|
+
validate_all_coords_available coords
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate_all_coords_available coords
|
93
|
+
coords.each do |coord|
|
94
|
+
fail 'Coordinate already occupied' unless grid[coord].empty?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_coord_not_shot coord
|
99
|
+
fail 'Coordinate has been shot already' if grid[coord].shot?
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Battleships::Cell
|
2
|
+
attr_accessor :content
|
3
|
+
|
4
|
+
def receive_shot
|
5
|
+
@shot = true
|
6
|
+
content.hit unless empty?
|
7
|
+
end
|
8
|
+
|
9
|
+
def shot?
|
10
|
+
@shot
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
!content
|
15
|
+
end
|
16
|
+
|
17
|
+
def status
|
18
|
+
if shot?
|
19
|
+
return empty? ? :miss : :hit
|
20
|
+
end
|
21
|
+
:none
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Battleships::CoordinateHandler
|
2
|
+
include Enumerable
|
3
|
+
|
4
|
+
HORIZONTAL_COORDS = ('A'..'J').to_a
|
5
|
+
VERTICAL_COORDS = (1..10).to_a
|
6
|
+
COORD_REGEX = /^([A-J])([1-9]|10)$/
|
7
|
+
|
8
|
+
def each
|
9
|
+
VERTICAL_COORDS.each do |y|
|
10
|
+
HORIZONTAL_COORDS.each do |x|
|
11
|
+
yield coordinate x, y
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def each_row
|
17
|
+
VERTICAL_COORDS.each do |y|
|
18
|
+
row = HORIZONTAL_COORDS.map do |x|
|
19
|
+
coordinate x, y
|
20
|
+
end
|
21
|
+
yield row, y
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate coord
|
26
|
+
fail 'Invalid coordinate' unless valid? coord
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid? coord
|
30
|
+
COORD_REGEX.match coord.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def from start, size, orientation = :horizontally
|
34
|
+
match = COORD_REGEX.match start
|
35
|
+
|
36
|
+
x = match.captures.first
|
37
|
+
y = match.captures.last
|
38
|
+
|
39
|
+
if orientation == :horizontally
|
40
|
+
horizontal_coords_for x, y, size
|
41
|
+
else
|
42
|
+
vertical_coords_for x, y, size
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def horizontal_coords_for x, y, size
|
49
|
+
x_coords(x, size).map { |x| coordinate x, y }
|
50
|
+
end
|
51
|
+
|
52
|
+
def x_coords x, size
|
53
|
+
start = HORIZONTAL_COORDS.index x
|
54
|
+
HORIZONTAL_COORDS.slice start, size
|
55
|
+
end
|
56
|
+
|
57
|
+
def vertical_coords_for x, y, size
|
58
|
+
y_coords(y, size).map { |y| coordinate x, y }
|
59
|
+
end
|
60
|
+
|
61
|
+
def y_coords y, size
|
62
|
+
start = VERTICAL_COORDS.index Integer(y)
|
63
|
+
VERTICAL_COORDS.slice start, size
|
64
|
+
end
|
65
|
+
|
66
|
+
def coordinate x, y
|
67
|
+
"#{x}#{y}".to_sym
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class Battleships::Game
|
2
|
+
BOARD_MARKERS = {
|
3
|
+
miss: '-',
|
4
|
+
hit: '*',
|
5
|
+
none: ' '
|
6
|
+
}.freeze
|
7
|
+
|
8
|
+
BOARD_TEMPLATE = <<TEMPLATE
|
9
|
+
ABCDEFGHIJ
|
10
|
+
------------
|
11
|
+
1|<1>|1
|
12
|
+
2|<2>|2
|
13
|
+
3|<3>|3
|
14
|
+
4|<4>|4
|
15
|
+
5|<5>|5
|
16
|
+
6|<6>|6
|
17
|
+
7|<7>|7
|
18
|
+
8|<8>|8
|
19
|
+
9|<9>|9
|
20
|
+
10|<10>|10
|
21
|
+
------------
|
22
|
+
ABCDEFGHIJ
|
23
|
+
TEMPLATE
|
24
|
+
|
25
|
+
BOARD_TEMPLATE.freeze
|
26
|
+
|
27
|
+
attr_reader :player_1, :player_2
|
28
|
+
|
29
|
+
def initialize(playerClass, boardClass)
|
30
|
+
@player_1 = initialize_player playerClass, boardClass
|
31
|
+
@player_2 = initialize_player playerClass, boardClass
|
32
|
+
|
33
|
+
|
34
|
+
player_1.opponent = player_2
|
35
|
+
player_2.opponent = player_1
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize_player(playerClass, boardClass)
|
39
|
+
player = playerClass.new
|
40
|
+
player.board = boardClass.new
|
41
|
+
player
|
42
|
+
end
|
43
|
+
|
44
|
+
def has_winner?
|
45
|
+
players.any?(&:winner?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def winner
|
49
|
+
players.find(&:winner?)
|
50
|
+
end
|
51
|
+
|
52
|
+
def own_board_view player
|
53
|
+
create_print player.board do |cell|
|
54
|
+
if cell.empty?
|
55
|
+
BOARD_MARKERS[cell.status]
|
56
|
+
else
|
57
|
+
cell.shot? ? BOARD_MARKERS[:hit] : cell.content.type.to_s.upcase[0]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def opponent_board_view player
|
63
|
+
create_print player.opponent.board do |cell|
|
64
|
+
BOARD_MARKERS[cell.status]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def players
|
71
|
+
[player_1, player_2]
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def create_print board
|
76
|
+
coord_handler = CoordinateHandler.new
|
77
|
+
|
78
|
+
output = BOARD_TEMPLATE
|
79
|
+
|
80
|
+
coord_handler.each_row do |row, number|
|
81
|
+
print_row = row.map do |coord|
|
82
|
+
yield board[coord]
|
83
|
+
end.join('')
|
84
|
+
|
85
|
+
output = output.sub("<#{number}>", print_row)
|
86
|
+
end
|
87
|
+
output
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Battleships::Player
|
2
|
+
attr_accessor :board, :opponent
|
3
|
+
|
4
|
+
def place_ship ship, coordinates, orientation = :horizontally
|
5
|
+
board.place_ship ship, coordinates, orientation
|
6
|
+
end
|
7
|
+
|
8
|
+
def receive_shot coordinates
|
9
|
+
fail 'Player has no board' unless board
|
10
|
+
board.receive_shot coordinates
|
11
|
+
end
|
12
|
+
|
13
|
+
def shoot coordinates
|
14
|
+
fail 'Player has no opponent' unless opponent
|
15
|
+
opponent.receive_shot coordinates
|
16
|
+
end
|
17
|
+
|
18
|
+
def winner?
|
19
|
+
fail 'Player has no opponent' unless opponent
|
20
|
+
opponent.all_ships_sunk?
|
21
|
+
end
|
22
|
+
|
23
|
+
def all_ships_sunk?
|
24
|
+
fail 'Player has no board' unless board
|
25
|
+
board.all_ships_sunk?
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Battleships::Ship
|
2
|
+
SIZES = {
|
3
|
+
submarine: 1,
|
4
|
+
destroyer: 2,
|
5
|
+
cruiser: 3,
|
6
|
+
battleship: 4,
|
7
|
+
aircraft_carrier: 5
|
8
|
+
}
|
9
|
+
|
10
|
+
attr_reader :type, :size
|
11
|
+
|
12
|
+
def initialize type
|
13
|
+
@type = type
|
14
|
+
@size = SIZES[type]
|
15
|
+
@hits = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def hit
|
19
|
+
@hits += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def sunk?
|
23
|
+
@hits >= size
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.submarine
|
27
|
+
Ship.new(:submarine)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.destroyer
|
31
|
+
Ship.new(:destroyer)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.cruiser
|
35
|
+
Ship.new(:cruiser)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.battleship
|
39
|
+
Ship.new(:battleship)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.aircraft_carrier
|
43
|
+
Ship.new(:aircraft_carrier)
|
44
|
+
end
|
45
|
+
end
|
data/lib/battleships.rb
ADDED
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: battleships
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Forrest
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-11 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple version of Battleships to play in irb
|
14
|
+
email: ben@silvabox.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/battleships.rb
|
20
|
+
- lib/battleships/board.rb
|
21
|
+
- lib/battleships/cell.rb
|
22
|
+
- lib/battleships/coordinate_handler.rb
|
23
|
+
- lib/battleships/game.rb
|
24
|
+
- lib/battleships/player.rb
|
25
|
+
- lib/battleships/ship.rb
|
26
|
+
homepage: https://github.com/silvabox/battleships
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.4.5
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Battleships
|
50
|
+
test_files: []
|