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 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
@@ -0,0 +1,8 @@
1
+ module Battleships; end
2
+
3
+ require_relative './battleships/board'
4
+ require_relative './battleships/ship'
5
+ require_relative './battleships/player'
6
+ require_relative './battleships/game'
7
+
8
+ include Battleships
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: []