sea_battle 0.1.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/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/bin/sea_battle +5 -0
- data/lib/sea_battle/board.rb +99 -0
- data/lib/sea_battle/cell.rb +51 -0
- data/lib/sea_battle/gui.rb +112 -0
- data/lib/sea_battle/random_ship.rb +84 -0
- data/lib/sea_battle/version.rb +3 -0
- data/lib/sea_battle.rb +65 -0
- data/sea_battle.gemspec +25 -0
- data/spec/sea_battle/board_spec.rb +55 -0
- data/spec/sea_battle/cell_spec.rb +51 -0
- data/spec/sea_battle/gui_spec.rb +50 -0
- data/spec/sea_battle/random_ship_spec.rb +98 -0
- data/spec/sea_battle_spec.rb +88 -0
- metadata +135 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 michal szyma
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# SeaBattle
|
2
|
+
|
3
|
+
Sea Battle is a guessing game for two players
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'sea_battle'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install sea_battle
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Initialize board of the game
|
22
|
+
|
23
|
+
$ require "sea_battle"
|
24
|
+
|
25
|
+
$ board = SeaBattle::Board.new.board
|
26
|
+
$ board[0][3].is_attacked?
|
27
|
+
|
28
|
+
Add random ship on the board
|
29
|
+
|
30
|
+
$ require "random_ship"
|
31
|
+
...
|
32
|
+
|
33
|
+
SeaBattle::RandomBoard.new(board, 3).add_ship
|
34
|
+
|
35
|
+
Play on console
|
36
|
+
|
37
|
+
$ sea_battle
|
38
|
+
|
39
|
+
#=> SEA BATTLE
|
40
|
+
|
41
|
+
#=> A B C D E F G H I J A B C D E F G H I J
|
42
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
43
|
+
#=> 0 │ │ │ │ │ │ │ │ ◯ │ │ │ 0 │ │ │ │ │ │ │ │ │ │ │
|
44
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
45
|
+
#=> 1 │ ◯ │ │ │ │ │ │ │ │ │ ◯ │ 1 │ │ │ │ │ │ │ │ │ │ │
|
46
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
47
|
+
#=> 2 │ ◯ │ │ │ │ │ │ │ │ │ ◯ │ 2 │ │ │ │ │ │ │ • │ • │ │ │
|
48
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
49
|
+
#=> 3 │ ◯ │ │ ⬤ │ ⬤ │ │ │ │ │ │ │ 3 │ │ │ │ │ │ │ │ • │ │ │
|
50
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
51
|
+
#=> 4 │ ⬤ │ │ │ │ │ ◯ │ │ │ │ │ 4 │ │ │ │ │ │ • │ │ │ │ │
|
52
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
53
|
+
#=> 5 │ │ │ ◯ │ │ │ ◯ │ │ │ │ ◯ │ 5 │ │ │ │ │ │ │ │ │ │ │
|
54
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
55
|
+
#=> 6 │ ◯ │ │ │ │ │ │ │ ◯ │ │ │ 6 │ │ │ │ │ │ │ │ │ │ │
|
56
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
57
|
+
#=> 7 │ │ │ ◯ │ │ │ │ │ ◯ │ │ │ 7 │ │ │ │ │ │ │ │ │ │ │
|
58
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
59
|
+
#=> 8 │ │ │ ◯ │ │ │ │ │ ◯ │ │ │ 8 │ │ │ │ │ │ │ │ │ │ │
|
60
|
+
#=> ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼
|
61
|
+
#=> 9 │ │ │ ◯ │ │ │ │ │ │ │ │ 9 │ │ │ │ │ │ │ │ │ │ │
|
62
|
+
|
63
|
+
#=> 4a, ... - select part of ship
|
64
|
+
#=> (e)xit of game
|
65
|
+
|
66
|
+
Use class SeaBattle
|
67
|
+
|
68
|
+
$ require "sea_battle"
|
69
|
+
|
70
|
+
$ first_board = SeaBattle::Board.new
|
71
|
+
$ second_board = SeaBattle::Board.new
|
72
|
+
$ SeaBattle.new(first_board, second_board)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/sea_battle
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative "cell"
|
4
|
+
require_relative "random_ship"
|
5
|
+
|
6
|
+
class SeaBattle
|
7
|
+
# It's Board of game Sea Battle
|
8
|
+
class Board
|
9
|
+
|
10
|
+
attr_reader :board, :vertical, :horizontal, :status
|
11
|
+
|
12
|
+
def initialize(board = "1" * 100, status = :initialized)
|
13
|
+
@board = board.split("").map do |status|
|
14
|
+
Cell.new(status.to_i)
|
15
|
+
end.each_slice(10).to_a
|
16
|
+
@horizontal = 10
|
17
|
+
@vertical = 10
|
18
|
+
|
19
|
+
@status = status
|
20
|
+
@quantity_ships = {1 => 4, 2 => 3, 3 => 2, 4 => 1}
|
21
|
+
check_board
|
22
|
+
end
|
23
|
+
|
24
|
+
def activated_board
|
25
|
+
@status = :activated
|
26
|
+
end
|
27
|
+
|
28
|
+
def attack(row, column)
|
29
|
+
board[row][column].attack
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_in_ship?(row, column)
|
33
|
+
board[row][column].is_in_ship?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set ships on the board (random positions)
|
37
|
+
def random_ships
|
38
|
+
return unless @status == :initialized
|
39
|
+
reset_board
|
40
|
+
[1, 1, 1, 1, 2, 2, 2, 3, 3, 4].each do |length|
|
41
|
+
RandomShip.new(self, length).add_ship
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return array of position ship if it will be on position [row, column]
|
46
|
+
def ship_positions(row, column)
|
47
|
+
horizontal = horizontal_ship_position(row, column)
|
48
|
+
vertical = vertical_ship_position(row, column)
|
49
|
+
return horizontal if horizontal.size > vertical.size
|
50
|
+
vertical
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# It should raise error when @board isn't Array
|
56
|
+
def check_board
|
57
|
+
raise "The board is not Array class" unless @board.size == 10
|
58
|
+
end
|
59
|
+
|
60
|
+
# it should return array of ship position
|
61
|
+
# for horizontal line with position [row, column]
|
62
|
+
def horizontal_ship_position(row, column)
|
63
|
+
result = []
|
64
|
+
10.times do |position|
|
65
|
+
if @board[position][column].is_in_ship?
|
66
|
+
result << [position, column]
|
67
|
+
else
|
68
|
+
break if result.include?([row, column])
|
69
|
+
result = []
|
70
|
+
end
|
71
|
+
end
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset_board
|
76
|
+
board.each do |line|
|
77
|
+
line.each do |cell|
|
78
|
+
cell.reset_cell
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# it should return array of ship position
|
84
|
+
# for vertical line with position [row, column]
|
85
|
+
def vertical_ship_position(row, column)
|
86
|
+
result = []
|
87
|
+
10.times do |position|
|
88
|
+
if @board[row][position].is_in_ship?
|
89
|
+
result << [row, position]
|
90
|
+
else
|
91
|
+
break if result.include?([row, column])
|
92
|
+
result = []
|
93
|
+
end
|
94
|
+
end
|
95
|
+
result
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class SeaBattle
|
4
|
+
# It's Cell of Board
|
5
|
+
class Cell
|
6
|
+
|
7
|
+
attr_reader :status
|
8
|
+
|
9
|
+
# status value:
|
10
|
+
# 1 -> empty field
|
11
|
+
# 2 -> field is part of ship
|
12
|
+
# 4 -> attacked field
|
13
|
+
# 8 -> is only selected by user
|
14
|
+
# 6 -> attacked field and exsist ship
|
15
|
+
def initialize(status = 1)
|
16
|
+
@status = status
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_ship
|
20
|
+
@status += 2 unless is_in_ship?
|
21
|
+
end
|
22
|
+
|
23
|
+
def attack
|
24
|
+
@status += 4 unless is_attacked?
|
25
|
+
end
|
26
|
+
|
27
|
+
def switch_select
|
28
|
+
is_selected? ? @status -= 8 : @status += 8
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_attacked?
|
32
|
+
@status & 4 == 4
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_empty?
|
36
|
+
@status & 1 == 1
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_in_ship?
|
40
|
+
@status & 2 == 2
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_selected?
|
44
|
+
@status & 8 == 8
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset_cell
|
48
|
+
@status = 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative "board"
|
4
|
+
require_relative "version"
|
5
|
+
require_relative "../sea_battle"
|
6
|
+
|
7
|
+
class SeaBattle
|
8
|
+
# User can play with computer on console
|
9
|
+
class GUI
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@user_board = ::SeaBattle::Board.new
|
13
|
+
@computer_board = ::SeaBattle::Board.new
|
14
|
+
@computer_board.random_ships
|
15
|
+
@computer_board.activated_board
|
16
|
+
@sea_battle = ::SeaBattle.new(@user_board, @computer_board)
|
17
|
+
set_default_keyboard
|
18
|
+
end
|
19
|
+
|
20
|
+
def play
|
21
|
+
while true and not @keyboard[:exit]
|
22
|
+
system "clear"
|
23
|
+
show_boards
|
24
|
+
get_input
|
25
|
+
if @keyboard[:position]
|
26
|
+
@sea_battle.move(:first_player, :attack, @row, @column)
|
27
|
+
system "clear"
|
28
|
+
show_boards
|
29
|
+
while @sea_battle.active_user == :second_player
|
30
|
+
sleep 1 + rand
|
31
|
+
@sea_battle.move(:second_player, :attack, rand(10), rand(10))
|
32
|
+
system "clear"
|
33
|
+
show_boards
|
34
|
+
end
|
35
|
+
@keyboard[:position] = false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def set_default_keyboard
|
43
|
+
@keyboard = {
|
44
|
+
exit: false,
|
45
|
+
activate: false,
|
46
|
+
position: nil
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def show_boards
|
51
|
+
show_title
|
52
|
+
|
53
|
+
puts ""
|
54
|
+
puts " A B C D E F G H I J A B C D E F G H I J"
|
55
|
+
@user_board.board.each_with_index do |line, index|
|
56
|
+
user_line = line.map do |cell|
|
57
|
+
if cell.is_attacked? and cell.is_in_ship?
|
58
|
+
" ⬤ "
|
59
|
+
elsif cell.is_attacked? and not cell.is_in_ship?
|
60
|
+
" • "
|
61
|
+
elsif not cell.is_attacked? and cell.is_in_ship?
|
62
|
+
" ◯ "
|
63
|
+
else
|
64
|
+
" "
|
65
|
+
end
|
66
|
+
end.join("│")
|
67
|
+
computer_line = @computer_board.board[index].map do |cell|
|
68
|
+
if cell.is_attacked? and cell.is_in_ship?
|
69
|
+
" ⬤ "
|
70
|
+
elsif cell.is_attacked? and not cell.is_in_ship?
|
71
|
+
" • "
|
72
|
+
else
|
73
|
+
" "
|
74
|
+
end
|
75
|
+
end.join("│")
|
76
|
+
puts " ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼ ┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼"
|
77
|
+
puts "#{index} │#{user_line}│ #{index} │#{computer_line}│"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def show_title
|
82
|
+
puts ""
|
83
|
+
puts "#{' '*10}SEA BATTLE ver#{VERSION}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_input
|
87
|
+
puts ""
|
88
|
+
puts "4a, ... - select part of ship"
|
89
|
+
unless @keyboard[:activate]
|
90
|
+
puts "(r)andom your ships on board"
|
91
|
+
puts "(a)ctivate your game if all ships are at properly place"
|
92
|
+
end
|
93
|
+
puts "(e)xit of game"
|
94
|
+
keyboard = gets.chomp
|
95
|
+
@keyboard[:exit] = true if keyboard == "e"
|
96
|
+
if not @keyboard[:exit] and @keyboard[:activate]
|
97
|
+
@keyboard[:position] = true
|
98
|
+
@row, @column = keyboard.split("")
|
99
|
+
@column = @column.ord - 97
|
100
|
+
@row = @row.to_i
|
101
|
+
end
|
102
|
+
if keyboard == "a"
|
103
|
+
|
104
|
+
@keyboard[:activate] = true
|
105
|
+
@user_board.activated_board
|
106
|
+
|
107
|
+
end
|
108
|
+
@user_board.random_ships if keyboard == "r"
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
class SeaBattle
|
5
|
+
# It's random positions for new ship
|
6
|
+
class RandomShip
|
7
|
+
|
8
|
+
def initialize(board, length = 1)
|
9
|
+
@board = board.board
|
10
|
+
@length = length
|
11
|
+
@horizontal = board.horizontal
|
12
|
+
@vertical = board.vertical
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add new Ship on board
|
16
|
+
def add_ship
|
17
|
+
if Random.rand(2) == 0
|
18
|
+
return if add_ship_of(:vertical)
|
19
|
+
return if add_ship_of(:horizontal)
|
20
|
+
else
|
21
|
+
return if add_ship_of(:horizontal)
|
22
|
+
return if add_ship_of(:vertical)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ship_positions(row, column, direct)
|
27
|
+
result = []
|
28
|
+
|
29
|
+
@length.times do |offset|
|
30
|
+
if direct.eql?(:vertical)
|
31
|
+
result << [row + offset, column] if row + offset < @vertical
|
32
|
+
else
|
33
|
+
result << [row, column + offset] if column + offset < @horizontal
|
34
|
+
end
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def area_ship_positions(row, column, direct)
|
40
|
+
result = []
|
41
|
+
first_row = row - 1
|
42
|
+
first_column = column - 1
|
43
|
+
|
44
|
+
last_row, last_column = row + 1, column + 1
|
45
|
+
last_row += @length - 1 if direct.eql?(:vertical)
|
46
|
+
last_column += @length - 1 if direct.eql?(:horizontal)
|
47
|
+
|
48
|
+
vertical_range = (first_row..last_row).to_a & (0...@vertical).to_a
|
49
|
+
horizontal_range = (first_column..last_column).to_a & (0...@horizontal).to_a
|
50
|
+
vertical_range.product(horizontal_range)
|
51
|
+
end
|
52
|
+
|
53
|
+
def mixed_board_positions
|
54
|
+
offset = Random.rand(@horizontal * @vertical)
|
55
|
+
(0...@vertical).to_a.product(
|
56
|
+
(0...@horizontal).to_a
|
57
|
+
).rotate(offset)
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_ship_of(direct)
|
61
|
+
mixed_board_positions.each do |row, column|
|
62
|
+
next if direct.eql?(:vertical) and row + @length - 1 >= @vertical
|
63
|
+
next if direct.eql?(:horizontal) and column + @length - 1 >= @horizontal
|
64
|
+
|
65
|
+
if is_area_empty?(row, column, direct)
|
66
|
+
positions = ship_positions(row, column, direct)
|
67
|
+
positions.map do |row, column|
|
68
|
+
@board[row][column].add_ship
|
69
|
+
end
|
70
|
+
return positions
|
71
|
+
end
|
72
|
+
end
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def is_area_empty?(row, column, direct)
|
77
|
+
area_ship_positions(row, column, direct).each do |row_index, column_index|
|
78
|
+
return false if @board[row_index][column_index].is_in_ship?
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
data/lib/sea_battle.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require "sea_battle/version"
|
2
|
+
require "sea_battle/board"
|
3
|
+
require "sea_battle/gui"
|
4
|
+
|
5
|
+
class SeaBattle
|
6
|
+
|
7
|
+
def initialize(first_board, second_board, last_attack_move = nil)
|
8
|
+
@last_attack_move = last_attack_move.split(";") unless last_attack_move.nil?
|
9
|
+
@first_board = first_board
|
10
|
+
@second_board = second_board
|
11
|
+
end
|
12
|
+
|
13
|
+
def active_user
|
14
|
+
return :first_player if @last_attack_move.nil?
|
15
|
+
player, row, column = @last_attack_move
|
16
|
+
if player.to_sym == :first_player
|
17
|
+
if @second_board.is_in_ship?(row.to_i, column.to_i)
|
18
|
+
return :first_player
|
19
|
+
else
|
20
|
+
return :second_player
|
21
|
+
end
|
22
|
+
else
|
23
|
+
if @first_board.is_in_ship?(row.to_i, column.to_i)
|
24
|
+
return :second_player
|
25
|
+
else
|
26
|
+
return :first_player
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def last_attack_move
|
32
|
+
return if @last_attack_move.nil?
|
33
|
+
@last_attack_move.join(";")
|
34
|
+
end
|
35
|
+
|
36
|
+
def move(player, type, row, column)
|
37
|
+
return false unless [:first_player, :second_player].include?(player)
|
38
|
+
return false unless [:choose, :attack, :mark].include?(type)
|
39
|
+
|
40
|
+
case type
|
41
|
+
when :attack
|
42
|
+
return false unless active_user == player
|
43
|
+
return false unless first_status.eql?(:activated) and second_status.eql?(:activated)
|
44
|
+
if player == :first_player
|
45
|
+
@second_board.attack(row, column)
|
46
|
+
else
|
47
|
+
@first_board.attack(row, column)
|
48
|
+
end
|
49
|
+
@last_attack_move = [player, row, column]
|
50
|
+
else
|
51
|
+
end
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def first_status
|
58
|
+
@first_board.status
|
59
|
+
end
|
60
|
+
|
61
|
+
def second_status
|
62
|
+
@second_board.status
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/sea_battle.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sea_battle/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sea_battle"
|
8
|
+
spec.version = SeaBattle::VERSION
|
9
|
+
spec.authors = ["Michał Szyma"]
|
10
|
+
spec.email = ["raglub.ruby@gmail.com"]
|
11
|
+
spec.description = %q{Sea Battle is a guessing game for two players}
|
12
|
+
spec.summary = %q{It is a popular game Battleship}
|
13
|
+
spec.homepage = "https://github.com/raglub/sea_battle"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_dependency "rspec", ">= 2.0.0"
|
23
|
+
spec.add_dependency "rspec_progress"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "sea_battle"
|
4
|
+
describe SeaBattle::Board do
|
5
|
+
|
6
|
+
let(:klass) { SeaBattle::Board }
|
7
|
+
|
8
|
+
it "should properly initialize class" do
|
9
|
+
expect { SeaBattle::Board.new }.to_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not properly initialize class" do
|
13
|
+
expect { SeaBattle::Board.new("") }.to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
context "should check method ship_positions"do
|
17
|
+
|
18
|
+
it "for non-ship" do
|
19
|
+
board = "0"*100
|
20
|
+
board[2] = "2"
|
21
|
+
board[3] = "2"
|
22
|
+
|
23
|
+
klass.new(board).ship_positions(1, 2).should eq([])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "for horizontal ship" do
|
27
|
+
board = "1"*100
|
28
|
+
board[2] = "2"
|
29
|
+
board[3] = "2"
|
30
|
+
klass.new(board).ship_positions(0, 2).should eq([[0, 2], [0, 3]])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "for vertical ship" do
|
34
|
+
board = "0"*100
|
35
|
+
board[23] = "2"
|
36
|
+
board[33] = "2"
|
37
|
+
klass.new(board).ship_positions(2, 3).should eq([[2, 3] ,[3, 3]])
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
context "should add new ship in random position" do
|
43
|
+
|
44
|
+
let(:board) { klass.new }
|
45
|
+
|
46
|
+
it "should spred randomly ships on the board" do
|
47
|
+
board.random_ships
|
48
|
+
result = 0
|
49
|
+
board.board.flatten.each do |cell|
|
50
|
+
result += 1 if cell.is_in_ship?
|
51
|
+
end
|
52
|
+
result.should eq(20)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "sea_battle"
|
4
|
+
|
5
|
+
describe SeaBattle::Cell do
|
6
|
+
|
7
|
+
let(:klass) { SeaBattle::Cell }
|
8
|
+
|
9
|
+
it "should properly initialize class" do
|
10
|
+
expect { klass.new(1) }.to_not raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should check method is_attacted?" do
|
14
|
+
klass.new(1).is_attacked?.should be_false
|
15
|
+
klass.new(4).is_attacked?.should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should check method attack" do
|
19
|
+
cell = klass.new(1)
|
20
|
+
cell.is_attacked?.should be_false
|
21
|
+
cell.attack
|
22
|
+
cell.is_attacked?.should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should check method is_empty?" do
|
26
|
+
klass.new(1).is_empty?.should be_true
|
27
|
+
klass.new(5).is_empty?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should check method is_in_ship?" do
|
31
|
+
klass.new(1).is_in_ship?.should be_false
|
32
|
+
klass.new(2).is_in_ship?.should be_true
|
33
|
+
klass.new(3).is_in_ship?.should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should check method is_selected?" do
|
37
|
+
klass.new(1).is_selected?.should be_false
|
38
|
+
klass.new(2).is_selected?.should be_false
|
39
|
+
klass.new(8).is_selected?.should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should check method switch_select" do
|
43
|
+
cell = klass.new(1)
|
44
|
+
cell.is_selected?.should be_false
|
45
|
+
cell.switch_select
|
46
|
+
cell.is_selected?.should be_true
|
47
|
+
cell.switch_select
|
48
|
+
cell.is_selected?.should be_false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "sea_battle"
|
4
|
+
def puts(message)
|
5
|
+
@@content ||= ""
|
6
|
+
@@content << message
|
7
|
+
end
|
8
|
+
|
9
|
+
def reset_content
|
10
|
+
@@content = ""
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
@@content
|
15
|
+
end
|
16
|
+
|
17
|
+
def sleep(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def system(value)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe SeaBattle::GUI do
|
24
|
+
|
25
|
+
it "should let user exit from game" do
|
26
|
+
gui = SeaBattle::GUI.new
|
27
|
+
gui.stub(:gets).and_return("e")
|
28
|
+
reset_content
|
29
|
+
gui.play
|
30
|
+
content.should include("(r)andom your ships on board")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should let to random user's ships" do
|
34
|
+
gui = SeaBattle::GUI.new
|
35
|
+
gui.stub(:gets).and_return("r", "e")
|
36
|
+
reset_content
|
37
|
+
gui.play
|
38
|
+
content.should include("◯")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should let user attacked field 2b" do
|
42
|
+
gui = SeaBattle::GUI.new
|
43
|
+
gui.stub(:gets).and_return("a", "2b", "7c", "3a", "e")
|
44
|
+
reset_content
|
45
|
+
gui.play
|
46
|
+
content.scan(/•|⬤/ ).should_not be_empty
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "sea_battle"
|
4
|
+
|
5
|
+
describe SeaBattle::RandomShip do
|
6
|
+
|
7
|
+
let(:board) { SeaBattle::Board.new("1"*100) }
|
8
|
+
|
9
|
+
let(:object) { klass.new(board) }
|
10
|
+
|
11
|
+
let(:klass) { SeaBattle::RandomShip }
|
12
|
+
|
13
|
+
context "#area_positions_with" do
|
14
|
+
it "for vertical in centre position" do
|
15
|
+
klass.new(board, 3).area_ship_positions(2, 3, :vertical).should eq([
|
16
|
+
[1, 2], [1, 3], [1, 4],
|
17
|
+
[2, 2], [2, 3], [2, 4],
|
18
|
+
[3, 2], [3, 3], [3, 4],
|
19
|
+
[4, 2], [4, 3], [4, 4],
|
20
|
+
[5, 2], [5, 3], [5, 4]
|
21
|
+
])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "for vertical in right position" do
|
25
|
+
klass.new(board, 1).area_ship_positions(5, 9, :vertical).should eq([
|
26
|
+
[4, 8], [4, 9],
|
27
|
+
[5, 8], [5, 9],
|
28
|
+
[6, 8], [6, 9]
|
29
|
+
])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "for horizontal in centre position" do
|
33
|
+
klass.new(board, 2).area_ship_positions(3, 4, :horizontal).should eq([
|
34
|
+
[2, 3], [2, 4], [2, 5], [2, 6],
|
35
|
+
[3, 3], [3, 4], [3, 5], [3, 6],
|
36
|
+
[4, 3], [4, 4], [4, 5], [4, 6]
|
37
|
+
])
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should check method ship_positions for vertical" do
|
43
|
+
klass.new(board, 3).ship_positions(2, 3, :vertical).should eq([
|
44
|
+
[2, 3], [3, 3], [4, 3]
|
45
|
+
])
|
46
|
+
|
47
|
+
klass.new(board, 3).ship_positions(2, 3, :horizontal).should eq([
|
48
|
+
[2, 3], [2, 4], [2, 5]
|
49
|
+
])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should check method is_area_empty?" do
|
53
|
+
klass.new(board, 2).is_area_empty?(3, 3, :vertical).should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should check method mixed_board_positions" do
|
57
|
+
Random.stub(:rand).with(10 * 10) { 34 }
|
58
|
+
klass.new(board, 4).mixed_board_positions.first.should eq([3, 4])
|
59
|
+
end
|
60
|
+
|
61
|
+
context "for method #add_ship_of(:vertical)"do
|
62
|
+
it "in centre of board" do
|
63
|
+
Random.stub(:rand).with(10 * 10) { 34 }
|
64
|
+
klass.new(board, 2).add_ship_of(:vertical).should eq([[3, 4], [4, 4]])
|
65
|
+
board.board[4][4].is_in_ship?.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "in bottom of board" do
|
69
|
+
Random.stub(:rand).with(10 * 10) { 93 }
|
70
|
+
klass.new(board, 2).add_ship_of(:vertical).should eq([[0, 0], [1, 0]])
|
71
|
+
board.board[1][0].is_in_ship?.should be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
it "in bottom of board" do
|
75
|
+
Random.stub(:rand).with(10 * 10) { 93 }
|
76
|
+
klass.new(board, 2).add_ship_of(:vertical).should eq([[0, 0], [1, 0]])
|
77
|
+
board.board[1][0].is_in_ship?.should be_true
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should check method #add_ship_of(:horizontal)" do
|
83
|
+
Random.stub(:rand).with(10 * 10) { 54 }
|
84
|
+
klass.new(board, 2).add_ship_of(:horizontal).should eq([[5, 4], [5, 5]])
|
85
|
+
board.board[5][5].is_in_ship?.should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should check method #add_ship" do
|
89
|
+
Random.stub(:rand).with(2) { 0 }
|
90
|
+
Random.stub(:rand).with(10 * 10) { 54 }
|
91
|
+
klass.new(board, 2).add_ship
|
92
|
+
board.board[6][4].is_in_ship?.should be_true
|
93
|
+
|
94
|
+
klass.new(board, 2).add_ship
|
95
|
+
board.board[6][6].is_in_ship?.should be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "sea_battle"
|
4
|
+
|
5
|
+
describe SeaBattle do
|
6
|
+
|
7
|
+
let(:board) { SeaBattle::Board }
|
8
|
+
|
9
|
+
# ABCDEFGHIJ
|
10
|
+
# 1 *
|
11
|
+
# 2 **** *
|
12
|
+
# 3
|
13
|
+
# 4 * *
|
14
|
+
# 5* * *
|
15
|
+
# 6 *
|
16
|
+
# 7
|
17
|
+
# 8 ***
|
18
|
+
# 9 ** *
|
19
|
+
# 10 * *
|
20
|
+
let(:first_raw_board) {
|
21
|
+
"1111111121" +
|
22
|
+
"1222211121" +
|
23
|
+
"1111111111" +
|
24
|
+
"1111112112" +
|
25
|
+
"2112112111" +
|
26
|
+
"1111112111" +
|
27
|
+
"1111111111" +
|
28
|
+
"1222111111" +
|
29
|
+
"1111122112" +
|
30
|
+
"1211111112"
|
31
|
+
}
|
32
|
+
it "should properly initialize class" do
|
33
|
+
expect { SeaBattle.new(board.new, board.new) }.to_not raise_error
|
34
|
+
end
|
35
|
+
|
36
|
+
context "for preparation game" do
|
37
|
+
it "should not attack player one if game isn't active" do
|
38
|
+
board_zero = board.new(first_raw_board, :initialized)
|
39
|
+
board_one = board.new(first_raw_board, :initialized)
|
40
|
+
sea_battle = SeaBattle.new(board_zero, board_one, nil)
|
41
|
+
sea_battle.move(:first_player, :attack, 2, 1)
|
42
|
+
sea_battle.last_attack_move.should eq(nil)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "for active game (first move)" do
|
47
|
+
let(:board_zero) { board.new(first_raw_board, :activated) }
|
48
|
+
let(:board_one) { board.new(first_raw_board, :activated) }
|
49
|
+
let(:sea_battle) { SeaBattle.new(board_zero, board_one, nil) }
|
50
|
+
|
51
|
+
it "should properly attack second player" do
|
52
|
+
sea_battle.move(:first_player, :attack, 1, 2).should be_true
|
53
|
+
sea_battle.last_attack_move.should eql("first_player;1;2")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "shouldn't properly attack first player" do
|
57
|
+
sea_battle.move(:second_player, :attack, 1, 2).should be_false
|
58
|
+
sea_battle.last_attack_move.should eql(nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should get first_player" do
|
62
|
+
sea_battle.active_user.should eql(:first_player)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "for active game and first player correctly attacked competition ship then" do
|
67
|
+
|
68
|
+
let(:board_zero) { board.new(first_raw_board, :activated) }
|
69
|
+
let(:board_one) { board.new(first_raw_board, :activated) }
|
70
|
+
let(:sea_battle) { SeaBattle.new(board_zero, board_one, "first_player;1;1") }
|
71
|
+
|
72
|
+
it "first player should properly attack second player" do
|
73
|
+
sea_battle.move(:first_player, :attack, 1, 2).should be_true
|
74
|
+
sea_battle.last_attack_move.should eql("first_player;1;2")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "second player shouldn't properly attack first player" do
|
78
|
+
sea_battle.move(:second_player, :attack, 1, 2).should be_false
|
79
|
+
sea_battle.last_attack_move.should eql("first_player;1;1")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should get first_player" do
|
83
|
+
sea_battle.active_user.should eql(:first_player)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sea_battle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michał Szyma
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.0.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec_progress
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Sea Battle is a guessing game for two players
|
79
|
+
email:
|
80
|
+
- raglub.ruby@gmail.com
|
81
|
+
executables:
|
82
|
+
- sea_battle
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE.txt
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- bin/sea_battle
|
93
|
+
- lib/sea_battle.rb
|
94
|
+
- lib/sea_battle/board.rb
|
95
|
+
- lib/sea_battle/cell.rb
|
96
|
+
- lib/sea_battle/gui.rb
|
97
|
+
- lib/sea_battle/random_ship.rb
|
98
|
+
- lib/sea_battle/version.rb
|
99
|
+
- sea_battle.gemspec
|
100
|
+
- spec/sea_battle/board_spec.rb
|
101
|
+
- spec/sea_battle/cell_spec.rb
|
102
|
+
- spec/sea_battle/gui_spec.rb
|
103
|
+
- spec/sea_battle/random_ship_spec.rb
|
104
|
+
- spec/sea_battle_spec.rb
|
105
|
+
homepage: https://github.com/raglub/sea_battle
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.24
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: It is a popular game Battleship
|
130
|
+
test_files:
|
131
|
+
- spec/sea_battle/board_spec.rb
|
132
|
+
- spec/sea_battle/cell_spec.rb
|
133
|
+
- spec/sea_battle/gui_spec.rb
|
134
|
+
- spec/sea_battle/random_ship_spec.rb
|
135
|
+
- spec/sea_battle_spec.rb
|