yakovenko_riverbattle 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +38 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/yakovenko_riverbattle/base.rb +3 -0
- data/lib/yakovenko_riverbattle/colorful.rb +26 -0
- data/lib/yakovenko_riverbattle/computer.rb +36 -0
- data/lib/yakovenko_riverbattle/constants.rb +14 -0
- data/lib/yakovenko_riverbattle/exit_error.rb +7 -0
- data/lib/yakovenko_riverbattle/field.rb +58 -0
- data/lib/yakovenko_riverbattle/game.rb +211 -0
- data/lib/yakovenko_riverbattle/human.rb +26 -0
- data/lib/yakovenko_riverbattle/invalid_move_error.rb +15 -0
- data/lib/yakovenko_riverbattle/move.rb +13 -0
- data/lib/yakovenko_riverbattle/player.rb +63 -0
- data/lib/yakovenko_riverbattle/version.rb +3 -0
- data/lib/yakovenko_riverbattle/victory_error.rb +43 -0
- data/lib/yakovenko_riverbattle.rb +5 -0
- data/yakovenko_riverbattle.gemspec +25 -0
- metadata +75 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d1eb9f00b43d823ef7e05bc63205d3d1fd8c6ce
|
4
|
+
data.tar.gz: 122c74ade649b2de1929f982c2f79717ff504343
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f488d1805b787509a3c1fa64fa9d1f75f31e34c974ad479a0ae0f395a9afd828547a25a8496b72c0308d30808497b221e36770a88884ae8769ef85f6e88c4edf
|
7
|
+
data.tar.gz: 167050d9af0a01ab7989a7bd58ea7796367a0073a8d035b47d5ae18152792159ceaa253e933b2041baab745803cc6e0cbafaaea2d6c46b38755f7751cf1342a0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Denis Yakovenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Denis Yakovenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Riverbattle
|
2
|
+
A simple console game.
|
3
|
+
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'yakovenko_riverbattle'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install yakovenko_riverbattle
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Development
|
26
|
+
|
27
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
28
|
+
|
29
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/yakovenko_riverbattle. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
34
|
+
|
35
|
+
|
36
|
+
## License
|
37
|
+
|
38
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "yakovenko_riverbattle"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
=begin
|
2
|
+
Adds special color functionality
|
3
|
+
to the `String` class. Decorated the
|
4
|
+
console output.
|
5
|
+
=end
|
6
|
+
class String
|
7
|
+
def black; "\033[30m#{self}\033[0m" end
|
8
|
+
def red; "\033[31m#{self}\033[0m" end
|
9
|
+
def green; "\033[32m#{self}\033[0m" end
|
10
|
+
def brown; "\033[33m#{self}\033[0m" end
|
11
|
+
def blue; "\033[34m#{self}\033[0m" end
|
12
|
+
def magenta; "\033[35m#{self}\033[0m" end
|
13
|
+
def cyan; "\033[36m#{self}\033[0m" end
|
14
|
+
def gray; "\033[37m#{self}\033[0m" end
|
15
|
+
def bg_black; "\033[40m#{self}\033[0m" end
|
16
|
+
def bg_red; "\033[41m#{self}\033[0m" end
|
17
|
+
def bg_green; "\033[42m#{self}\033[0m" end
|
18
|
+
def bg_brown; "\033[43m#{self}\033[0m" end
|
19
|
+
def bg_blue; "\033[44m#{self}\033[0m" end
|
20
|
+
def bg_magenta; "\033[45m#{self}\033[0m" end
|
21
|
+
def bg_cyan; "\033[46m#{self}\033[0m" end
|
22
|
+
def bg_gray; "\033[47m#{self}\033[0m" end
|
23
|
+
def bold; "\033[1m#{self}\033[22m" end
|
24
|
+
def reverse_color; "\033[7m#{self}\033[27m" end
|
25
|
+
def blink; "\e[5m#{self}\e[0m" end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require './player'
|
2
|
+
require './constants'
|
3
|
+
require './invalid_move_error'
|
4
|
+
|
5
|
+
=begin
|
6
|
+
Represents the Computer player
|
7
|
+
of the game. Does random hits on
|
8
|
+
the opponents field using the
|
9
|
+
initially generated array of random numbers.
|
10
|
+
=end
|
11
|
+
class Computer < Player
|
12
|
+
|
13
|
+
# Include the special constants module
|
14
|
+
# that contains symbols for hits and mistakes
|
15
|
+
# player's moves and smiles for victory and defeat.
|
16
|
+
include Symbols
|
17
|
+
|
18
|
+
def initialize field
|
19
|
+
super field
|
20
|
+
@moves = (1..10).to_a.shuffle.take(10)
|
21
|
+
@move_count = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Makes a move to the random
|
25
|
+
# cell of the opponent's field
|
26
|
+
# `opponent_field` - the field object
|
27
|
+
# of the opponent.
|
28
|
+
def move opponent_field
|
29
|
+
index = @moves[@move_count]
|
30
|
+
move = opponent_field.receive_move(index)
|
31
|
+
modify_opponent_field_map(index, move)
|
32
|
+
@move_count += 1
|
33
|
+
@moves << move
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
=begin
|
2
|
+
The module that contains
|
3
|
+
special symbols for hits and mistakes of
|
4
|
+
player's moves and smiles for victory and defeat.
|
5
|
+
To change the look of the outputted symbols in the
|
6
|
+
game you can only change them here.
|
7
|
+
=end
|
8
|
+
module Symbols
|
9
|
+
HIT = '☠'
|
10
|
+
SHIP = 'X'
|
11
|
+
MISS = '·'
|
12
|
+
LOOSE = '☹'
|
13
|
+
WIN = '☺'
|
14
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require './colorful'
|
2
|
+
require './move'
|
3
|
+
require './invalid_move_error'
|
4
|
+
|
5
|
+
=begin
|
6
|
+
Represents the game field object.
|
7
|
+
Contains specialized methods for rendering
|
8
|
+
the field and receiving the player's moves.
|
9
|
+
=end
|
10
|
+
class Field
|
11
|
+
|
12
|
+
# Two-dimensional array that
|
13
|
+
# represents the actual game field
|
14
|
+
# as it appears on the console screen.
|
15
|
+
attr_reader :field
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@field = [[' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
19
|
+
['a',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ']]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Draws the game field on the console
|
23
|
+
# using cyan background color.
|
24
|
+
def draw
|
25
|
+
field.each do |arr|
|
26
|
+
arr.each { |i| print " #{i} ".black.bg_cyan }
|
27
|
+
puts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the array that
|
32
|
+
# consists of all the moves that
|
33
|
+
# the current game field received.
|
34
|
+
def moves_array
|
35
|
+
@field[1]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks if the `index` parameter,
|
39
|
+
# that represents the number of the cell
|
40
|
+
# on the game field to shoot, actually
|
41
|
+
# hits the ship. Returns the `Move` object.
|
42
|
+
def receive_move index
|
43
|
+
hit = !(@field[1][index] =~ /X/).nil?
|
44
|
+
Move.new(index, hit)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Setter created for the convenience
|
48
|
+
# of the class usage.
|
49
|
+
def []=(index, value)
|
50
|
+
@field[1][index] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def [](index)
|
54
|
+
@field[1][index]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require './field'
|
2
|
+
require './human'
|
3
|
+
require './computer'
|
4
|
+
require './colorful'
|
5
|
+
require './constants'
|
6
|
+
require './exit_error'
|
7
|
+
require './victory_error'
|
8
|
+
require './invalid_move_error'
|
9
|
+
|
10
|
+
=begin
|
11
|
+
This is the main class of the game that
|
12
|
+
starts the game itself. To run the game
|
13
|
+
use method `start`.
|
14
|
+
The class deals with players' initialization,
|
15
|
+
fields' rendering and manages players' moves.
|
16
|
+
=end
|
17
|
+
class Game
|
18
|
+
|
19
|
+
# Include the special constants module
|
20
|
+
# that contains symbols for hits and mistakes
|
21
|
+
# player's moves and smiles for victory and defeat.
|
22
|
+
include Symbols
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
init_players
|
26
|
+
end
|
27
|
+
|
28
|
+
# Main method of the class. Starts the infinite
|
29
|
+
# loop of the game. Constantly checks for user's
|
30
|
+
# input and manages players' moves.
|
31
|
+
def start
|
32
|
+
message = get_instructions + "Enter which cell to shoot: ".cyan.bold
|
33
|
+
message_norm = message
|
34
|
+
message_arg_error = "Please, enter only numbers between 1 and 10".red.bold
|
35
|
+
message_alert = message_arg_error + "\n" + message
|
36
|
+
begin
|
37
|
+
loop do
|
38
|
+
draw_game_field
|
39
|
+
print message
|
40
|
+
message = message_norm
|
41
|
+
cell = gets.chomp
|
42
|
+
raise Exception if cell == 'q'
|
43
|
+
cell = Integer(cell)
|
44
|
+
raise ArgumentError unless cell.between?(1, 10)
|
45
|
+
@human.move(cell, @computer.field)
|
46
|
+
@computer.move(@human.field)
|
47
|
+
draw_game_field
|
48
|
+
check_victory
|
49
|
+
end
|
50
|
+
rescue ArgumentError => e
|
51
|
+
message = message_alert
|
52
|
+
puts message
|
53
|
+
retry
|
54
|
+
rescue VictoryError => e
|
55
|
+
puts e.message
|
56
|
+
exit 1
|
57
|
+
rescue StandardError => e
|
58
|
+
retry
|
59
|
+
rescue Exception => e
|
60
|
+
exit_the_game
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Checks for the victory of either of
|
67
|
+
# players and raises the special VictoryError
|
68
|
+
# that exits the game outputting the winner's
|
69
|
+
# greeting.
|
70
|
+
def check_victory
|
71
|
+
my_hits = @human.opponent_field_map.moves_array.count { |x|
|
72
|
+
!(x.to_s =~ /#{Symbols::HIT}/).nil?
|
73
|
+
}
|
74
|
+
my_ships = @human.field.moves_array.count { |x|
|
75
|
+
!(x.to_s =~ /#{Symbols::SHIP}/).nil?
|
76
|
+
}
|
77
|
+
opponent_ships = @computer.field.moves_array.count { |x|
|
78
|
+
!(x.to_s =~ /#{Symbols::SHIP}/).nil?
|
79
|
+
}
|
80
|
+
comp_hits = @computer.opponent_field_map.moves_array.count { |x|
|
81
|
+
!(x.to_s =~ /#{Symbols::HIT}/).nil?
|
82
|
+
}
|
83
|
+
# check for the human victory
|
84
|
+
raise VictoryError.new(@human) if my_hits == opponent_ships
|
85
|
+
# check for the computer victory
|
86
|
+
raise VictoryError.new(@computer) if comp_hits == my_ships
|
87
|
+
end
|
88
|
+
|
89
|
+
# Creates the objects of both players.
|
90
|
+
# Initiates the game field for both of them.
|
91
|
+
def init_players
|
92
|
+
@human = Human.new("Jack", Field.new)
|
93
|
+
@computer = Computer.new(Field.new)
|
94
|
+
init_fields
|
95
|
+
end
|
96
|
+
|
97
|
+
# Initiates the game fields for the two
|
98
|
+
# players separately and places the ships
|
99
|
+
# of each player on their game field.
|
100
|
+
def init_fields
|
101
|
+
init_human_field
|
102
|
+
init_computer_field
|
103
|
+
place_ships_on_the_field(@human.ships, @human.field)
|
104
|
+
place_ships_on_the_field(@computer.ships, @computer.field)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Renders the three fields on the console:
|
108
|
+
# the human player's field, the field of
|
109
|
+
# moves of the computer and the field of
|
110
|
+
# the human player's moves.
|
111
|
+
def draw_game_field
|
112
|
+
system 'clear'
|
113
|
+
puts "My field:".green.bold
|
114
|
+
@human.field.draw
|
115
|
+
puts "\nComputer's moves:".blue.bold
|
116
|
+
@computer.draw_opponent_field_map
|
117
|
+
puts "\nMy moves:".blue.bold
|
118
|
+
@human.draw_opponent_field_map
|
119
|
+
end
|
120
|
+
|
121
|
+
# Asks the human player for how many
|
122
|
+
# ships they're going to have (maximum 10).
|
123
|
+
# The computer will have the same number of ships
|
124
|
+
# generated randomly.
|
125
|
+
def get_ships_number
|
126
|
+
system 'clear'
|
127
|
+
message = "Enter the number of your ships (10 max): ".green
|
128
|
+
message_error = "You are allowed to enter only numbers between 1 and 10.\nPlease, try again.".red
|
129
|
+
message_alert = message_error + "\n" + message
|
130
|
+
begin
|
131
|
+
print message
|
132
|
+
@ships_number = gets.chomp
|
133
|
+
raise ExitError if @ships_number == 'q'
|
134
|
+
@ships_number = Integer(@ships_number)
|
135
|
+
raise Exception unless @ships_number.between?(1, 10)
|
136
|
+
rescue ExitError => e
|
137
|
+
exit_the_game
|
138
|
+
rescue Exception => e
|
139
|
+
message = message_alert
|
140
|
+
retry
|
141
|
+
end
|
142
|
+
puts "\rThe number of your ships will be #{@ships_number}".cyan
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
# Returns the string of special
|
149
|
+
# instructions and hints for the human
|
150
|
+
# player.
|
151
|
+
def get_instructions
|
152
|
+
instructions = <<-TEXT
|
153
|
+
Field symbols:
|
154
|
+
#{Symbols::HIT} --> Hit
|
155
|
+
#{Symbols::MISS} --> Miss
|
156
|
+
#{Symbols::SHIP} --> Your ship
|
157
|
+
|
158
|
+
Game control:
|
159
|
+
q --> Exit
|
160
|
+
|
161
|
+
TEXT
|
162
|
+
end
|
163
|
+
|
164
|
+
# After asking the human player
|
165
|
+
# for the number of ships, the method
|
166
|
+
# `init_human_field` is called. In a loop
|
167
|
+
# it asks the player to choose which cell of
|
168
|
+
# the field they want to place their ships to.
|
169
|
+
# Validates the input.
|
170
|
+
def init_human_field
|
171
|
+
get_ships_number
|
172
|
+
system 'clear'
|
173
|
+
message = "Enter the number of the cell for your: ".green
|
174
|
+
message_error = "You are allowed to enter only numbers between 1 and 10.\nPlease, try again.".red
|
175
|
+
message_alert = message_error + "\n" + message
|
176
|
+
i = 1
|
177
|
+
begin
|
178
|
+
message = "Enter the number of the cell for your #{i} ship (1-10): ".green
|
179
|
+
print message
|
180
|
+
input = gets.chomp
|
181
|
+
raise ExitError if input == 'q'
|
182
|
+
input = Integer(input)
|
183
|
+
raise Exception unless input.between?(1, 10) && @human.add_ship(input)
|
184
|
+
i += 1
|
185
|
+
rescue ExitError => e
|
186
|
+
exit_the_game
|
187
|
+
rescue Exception => e
|
188
|
+
puts "Enter only unique numbers between 1 and 10".red
|
189
|
+
retry
|
190
|
+
end until i == @ships_number + 1
|
191
|
+
end
|
192
|
+
|
193
|
+
# Allocates computer's ships randomly
|
194
|
+
# based on the number of human player's ships
|
195
|
+
def init_computer_field
|
196
|
+
@computer.ships = (1..10).to_a.shuffle.take(@ships_number)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Places the ships to the actual game field
|
200
|
+
def place_ships_on_the_field ships, field
|
201
|
+
ships.each do |ship|
|
202
|
+
if ship.to_i == 10 then field[ship] = Symbols::SHIP + ' '
|
203
|
+
else field[ship] = Symbols::SHIP
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def exit_the_game
|
209
|
+
abort ("\n Exiting the game...".magenta + "\n Have a nice day!\n".blue.bold)
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require './player'
|
2
|
+
require './constants'
|
3
|
+
require './invalid_move_error'
|
4
|
+
|
5
|
+
class Human < Player
|
6
|
+
|
7
|
+
# Include the special constants module
|
8
|
+
# that contains symbols for hits and mistakes
|
9
|
+
# player's moves and smiles for victory and defeat.
|
10
|
+
include Symbols
|
11
|
+
|
12
|
+
def initialize name, field
|
13
|
+
@name = name
|
14
|
+
super field
|
15
|
+
end
|
16
|
+
|
17
|
+
# Makes a move on the opponent's field
|
18
|
+
# and raises the error if the `index`
|
19
|
+
# parameter is not a number in range from 1 to 10.
|
20
|
+
# `opponent_field` - the field object of the opponent.
|
21
|
+
def move index, opponent_field
|
22
|
+
move = opponent_field.receive_move(index)
|
23
|
+
raise InvalidMoveError unless (validates_move(move) && @moves << move)
|
24
|
+
modify_opponent_field_map(index, move)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
=begin
|
2
|
+
The specially created error that is
|
3
|
+
raised when the wrong or improper
|
4
|
+
move is done by the player.
|
5
|
+
=end
|
6
|
+
class InvalidMoveError < StandardError
|
7
|
+
|
8
|
+
def initialize(msg = nil)
|
9
|
+
@message = msg
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
"You cannot make that move. #{@message}"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
=begin
|
2
|
+
Represents the move on the field.
|
3
|
+
Created for being able to contain
|
4
|
+
the information about the accuracy of
|
5
|
+
the player's move.
|
6
|
+
=end
|
7
|
+
class Move
|
8
|
+
attr_reader :hit, :index
|
9
|
+
|
10
|
+
def initialize index, hit
|
11
|
+
@hit, @index = hit, index
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require './move'
|
2
|
+
require './field'
|
3
|
+
require './invalid_move_error'
|
4
|
+
|
5
|
+
|
6
|
+
=begin
|
7
|
+
The class that represents the player
|
8
|
+
of the game and contains the basic
|
9
|
+
method that are used by both human player's
|
10
|
+
and computers. Responsible for move validation
|
11
|
+
and the game's field modification.
|
12
|
+
=end
|
13
|
+
class Player
|
14
|
+
|
15
|
+
# :field - the actual game field of the player
|
16
|
+
# :opponent_field_map - the auxiliary field object
|
17
|
+
# that helps the player to make moves. Represents the
|
18
|
+
# co-called imaginary opponent's field. This object is
|
19
|
+
# the one that is actually modified and rendered.
|
20
|
+
# :moves - contains the array of the moves of the player.
|
21
|
+
# :ships - contains the array of the ships of the player.
|
22
|
+
attr_accessor :field, :opponent_field_map, :moves, :ships
|
23
|
+
|
24
|
+
def initialize field
|
25
|
+
@field = field
|
26
|
+
@opponent_field_map = Field.new
|
27
|
+
@moves = []
|
28
|
+
@ships = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds the ship to the array of the ships and
|
32
|
+
# raises the error if such move cannot be done
|
33
|
+
# i.e. the ship has already been placed.
|
34
|
+
def add_ship value
|
35
|
+
raise InvalidMoveError if @ships.include?(value)
|
36
|
+
@ships.push(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Draws the imaginary opponent field
|
40
|
+
# on the console.
|
41
|
+
def draw_opponent_field_map
|
42
|
+
@opponent_field_map.draw
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Validates the move in such a way that
|
48
|
+
# in should be unique (for the efficiency) and
|
49
|
+
# be in the range from 1 to 10.
|
50
|
+
def validates_move move
|
51
|
+
!@moves.include?(move) && move.index.between?(1,10)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Modifies the imaginary field of the opponent
|
55
|
+
# with a special symbol that represent either hit or miss
|
56
|
+
# of the move made.
|
57
|
+
def modify_opponent_field_map index, move
|
58
|
+
hit = index == 10 ? "#{Symbols::HIT} " : Symbols::HIT
|
59
|
+
miss = index == 10 ? "#{Symbols::MISS} " : Symbols::MISS
|
60
|
+
@opponent_field_map[index] = move.hit ? hit : miss
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require './human'
|
2
|
+
require './colorful'
|
3
|
+
require './computer'
|
4
|
+
require './constants'
|
5
|
+
|
6
|
+
=begin
|
7
|
+
The specially created error that
|
8
|
+
is raised when someone's victory
|
9
|
+
takes place. Receives the object of
|
10
|
+
the winner as an input parameter.
|
11
|
+
Contains the special message for the victory.
|
12
|
+
=end
|
13
|
+
class VictoryError < StandardError
|
14
|
+
|
15
|
+
# Include the special constants module
|
16
|
+
# that contains symbols for hits and mistakes
|
17
|
+
# player's moves and smiles for victory and defeat.
|
18
|
+
include Symbols
|
19
|
+
|
20
|
+
def initialize(winner)
|
21
|
+
@winner = winner
|
22
|
+
end
|
23
|
+
|
24
|
+
# Generates the special greeting message
|
25
|
+
# for the winner of the game,
|
26
|
+
def message
|
27
|
+
if(@winner.is_a? Computer)
|
28
|
+
loose = (Symbols::LOOSE + ' ') * 3
|
29
|
+
@message = <<-TEXT
|
30
|
+
#{'Computer wins!'.bold.blue}
|
31
|
+
#{(loose + 'You loose...' + loose).bold.magenta}\n
|
32
|
+
TEXT
|
33
|
+
elsif(@winner.is_a? Human)
|
34
|
+
win = (Symbols::WIN + ' ') * 3
|
35
|
+
@message = <<-TEXT
|
36
|
+
#{'Computer sucks!'.bold.magenta}
|
37
|
+
#{(win + 'You win!' + win).bold.cyan}\n
|
38
|
+
TEXT
|
39
|
+
end
|
40
|
+
@message
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -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 'yakovenko_riverbattle/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "yakovenko_riverbattle"
|
8
|
+
spec.version = YakovenkoRiverbattle::VERSION
|
9
|
+
spec.authors = ["Denis Yakovenko"]
|
10
|
+
spec.email = ["yakovenko.denis.a@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Enjoy the game!}
|
13
|
+
spec.description = %q{TA small simple game called the Riverbattle.}
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = '>= 2.0'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
metadata
CHANGED
@@ -1,22 +1,90 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yakovenko_riverbattle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Yakovenko
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2015-07-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: TA small simple game called the Riverbattle.
|
14
56
|
email:
|
15
57
|
- yakovenko.denis.a@gmail.com
|
16
58
|
executables: []
|
17
59
|
extensions: []
|
18
60
|
extra_rdoc_files: []
|
19
|
-
files:
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".travis.yml"
|
65
|
+
- CODE_OF_CONDUCT.md
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE
|
68
|
+
- LICENSE.txt
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- bin/console
|
72
|
+
- bin/setup
|
73
|
+
- lib/yakovenko_riverbattle.rb
|
74
|
+
- lib/yakovenko_riverbattle/base.rb
|
75
|
+
- lib/yakovenko_riverbattle/colorful.rb
|
76
|
+
- lib/yakovenko_riverbattle/computer.rb
|
77
|
+
- lib/yakovenko_riverbattle/constants.rb
|
78
|
+
- lib/yakovenko_riverbattle/exit_error.rb
|
79
|
+
- lib/yakovenko_riverbattle/field.rb
|
80
|
+
- lib/yakovenko_riverbattle/game.rb
|
81
|
+
- lib/yakovenko_riverbattle/human.rb
|
82
|
+
- lib/yakovenko_riverbattle/invalid_move_error.rb
|
83
|
+
- lib/yakovenko_riverbattle/move.rb
|
84
|
+
- lib/yakovenko_riverbattle/player.rb
|
85
|
+
- lib/yakovenko_riverbattle/version.rb
|
86
|
+
- lib/yakovenko_riverbattle/victory_error.rb
|
87
|
+
- yakovenko_riverbattle.gemspec
|
20
88
|
homepage:
|
21
89
|
licenses:
|
22
90
|
- MIT
|
@@ -29,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
29
97
|
requirements:
|
30
98
|
- - ">="
|
31
99
|
- !ruby/object:Gem::Version
|
32
|
-
version: '0'
|
100
|
+
version: '2.0'
|
33
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
102
|
requirements:
|
35
103
|
- - ">="
|