sea_battle 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|