ruby_arena 0.0.2

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +31 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +118 -0
  8. data/Rakefile +5 -0
  9. data/bin/ruby_arena +7 -0
  10. data/lib/ruby_arena/ai.rb +68 -0
  11. data/lib/ruby_arena/arena.rb +75 -0
  12. data/lib/ruby_arena/bullet.rb +53 -0
  13. data/lib/ruby_arena/bullet_renderer.rb +53 -0
  14. data/lib/ruby_arena/class_loader.rb +20 -0
  15. data/lib/ruby_arena/command_parser.rb +41 -0
  16. data/lib/ruby_arena/game.rb +74 -0
  17. data/lib/ruby_arena/gui.rb +62 -0
  18. data/lib/ruby_arena/level.rb +42 -0
  19. data/lib/ruby_arena/level0.rb +6 -0
  20. data/lib/ruby_arena/level1.rb +9 -0
  21. data/lib/ruby_arena/movable.rb +18 -0
  22. data/lib/ruby_arena/option_parser.rb +29 -0
  23. data/lib/ruby_arena/robot.rb +196 -0
  24. data/lib/ruby_arena/robot_renderer.rb +180 -0
  25. data/lib/ruby_arena/value_sanitizer.rb +11 -0
  26. data/lib/ruby_arena/version.rb +3 -0
  27. data/lib/ruby_arena.rb +23 -0
  28. data/ruby_arena.gemspec +25 -0
  29. data/screenshot.png +0 -0
  30. data/spec/ai_spec.rb +11 -0
  31. data/spec/ai_test.rb +11 -0
  32. data/spec/arena_spec.rb +79 -0
  33. data/spec/bullet_renderer_spec.rb +4 -0
  34. data/spec/bullet_spec.rb +36 -0
  35. data/spec/class_loader_spec.rb +14 -0
  36. data/spec/command_parser_spec.rb +72 -0
  37. data/spec/fake_gui.rb +28 -0
  38. data/spec/game_spec.rb +4 -0
  39. data/spec/gui_spec.rb +50 -0
  40. data/spec/level_spec.rb +29 -0
  41. data/spec/movable_tests.rb +48 -0
  42. data/spec/option_parser_spec.rb +49 -0
  43. data/spec/robot_renderer_spec.rb +4 -0
  44. data/spec/robot_spec.rb +294 -0
  45. data/spec/spec_helper.rb +8 -0
  46. data/spec/value_sanitizer_spec.rb +21 -0
  47. metadata +163 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7683767c61f9ba12e956de9c4a7ddaff97653fc
4
+ data.tar.gz: 6b613e090ef7477e2c0b6bd0f4c4680fdbc67881
5
+ SHA512:
6
+ metadata.gz: 3b358f8e27cecf896b8bdfd39400a5fe623d0a662505ebe63f0020f909f8e44e52c20116c2f276d95ff034903f0c42427dbe0b529922236fdb9262686465f69b
7
+ data.tar.gz: ae3790d093b6a7597cd72d92cba6b7835d21e6de8d417cb741d683de54be98304221879ccc8fa16a53da05b726a58e9c3ef0be276bdf1926a2c298ae32e9fa25
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /.rspec
2
+ /test_robots/*
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 2.1.0
3
+ before_install:
4
+ - sudo apt-get update -qq
5
+ - sudo apt-get install -qq build-essential freeglut3-dev libfreeimage-dev libgl1-mesa-dev libopenal-dev libpango1.0-dev libsdl-ttf2.0-dev libsndfile-dev libxinerama-dev
6
+ script:
7
+ - bundle exec rspec spec/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby_arena (0.0.1.alpha)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ gosu (0.7.50)
11
+ rake (10.1.1)
12
+ rspec (3.0.0.beta1)
13
+ rspec-core (= 3.0.0.beta1)
14
+ rspec-expectations (= 3.0.0.beta1)
15
+ rspec-mocks (= 3.0.0.beta1)
16
+ rspec-core (3.0.0.beta1)
17
+ rspec-expectations (3.0.0.beta1)
18
+ diff-lcs (>= 1.1.3, < 2.0)
19
+ rspec-support (= 3.0.0.beta1)
20
+ rspec-mocks (3.0.0.beta1)
21
+ rspec-support (3.0.0.beta1)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (~> 1.5)
28
+ gosu
29
+ rake
30
+ rspec (~> 3.0.0.beta1)
31
+ ruby_arena!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Patrik Bóna
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,118 @@
1
+ # ruby_arena
2
+ [![Code
3
+ Climate](https://codeclimate.com/github/mrhead/ruby_arena.png)](https://codeclimate.com/github/mrhead/ruby_arena)
4
+ [![Build Status](https://travis-ci.org/mrhead/ruby_arena.png)](https://travis-ci.org/mrhead/ruby_arena)
5
+
6
+ ![Screenshot from the game](https://github.com/mrhead/ruby_arena/blob/master/screenshot.png?raw=true)
7
+
8
+ Ruby Arena is a game for programmers and people who wants to learn how to program in Ruby.
9
+
10
+ Instead of controling your robot with keys you have to program its AI (artifical intelligence) and see if it can beat other robots!
11
+
12
+ This project has been inspired by [robocode][robocode] (Java, .NET) and [rrobots][rrobots] (Ruby).
13
+
14
+ ## Goals of the project
15
+
16
+ - create arena from scratch
17
+ - learn
18
+ - have fun!
19
+
20
+ ## How to run arena (and battles)
21
+
22
+ ```
23
+ git@github.com:mrhead/ruby_arena.git
24
+ cd ruby_arena
25
+ bundle # or gem install gosu
26
+ ./bin/ruby_arena.rb path_to/robot.rb path_to/another_robot.rb
27
+ ```
28
+
29
+ You can run game with some predefined robots (levels).
30
+
31
+ ```
32
+ ./bin/ruby_arena.rb -l 0 path_to/your_ai.rb
33
+ ```
34
+
35
+ You can see example robot here: https://gist.github.com/mrhead/7917528. Just put it to some dir in ruby_arena.
36
+
37
+ ```
38
+ ./bin/ruby_arena.rb test_robots/test_ai.rb test_robots/test_ai.rb # ...
39
+ ```
40
+
41
+ ### Example robots (AIs)
42
+
43
+ If you have your own robot (well AI), then please publish it as gist and then add it here:
44
+
45
+ * https://gist.github.com/mrhead/7917528
46
+ * https://gist.github.com/mrhead/8086991
47
+ * https://gist.github.com/mrhead/8087011
48
+
49
+ Please note that mentioned Ais are not were nice (code) and pretty stupid right now, but at least you can try to run them.
50
+
51
+ ### How to create your own robot (AI)
52
+
53
+ Just extend `Ai` class and implement your own `#tick` method.
54
+
55
+ ```
56
+ class TestAi < Ai
57
+ def tick(events)
58
+ fire # do nothing but fire when possible
59
+ end
60
+ end
61
+ ```
62
+
63
+ ### API for AI
64
+
65
+ AI api is very similar to robot api from [rrobots][rrobots].
66
+
67
+ ```
68
+ # change speed by 1, max to 8
69
+ accelerate
70
+
71
+ # change speed by -1, min to -8
72
+ decelerate
73
+
74
+ # turn robot, gun and radar
75
+ turn(angle)
76
+
77
+ # turn gun and radar
78
+ turn_gun(angle)
79
+
80
+ # turn radar
81
+ turn_radar(angle)
82
+
83
+ # fire a bullet
84
+ fire
85
+
86
+ # change radar view angle
87
+ set_radar_view_angle(angle)
88
+ ```
89
+
90
+ You can do each action just once in each tick, so:
91
+
92
+ ```
93
+ turn(2)
94
+ turn(2)
95
+ ```
96
+
97
+ Will do just turn by 2 degrees. In is the same for other actions.
98
+
99
+ ## Current status
100
+
101
+ Project is in very early stage with unstable API. Any contribution (your own robot, reported issue, new pull request, advice regarding code/OO design) is very much appreciated.
102
+
103
+ However you can already start the game and the battle between robots! Detais were mentioned above.
104
+
105
+ ## How to contribute
106
+
107
+ 1. Fork the repo
108
+ 2. Create your own branch
109
+ 3. Do your changes
110
+ 4. Create pull request
111
+
112
+ ### Rules for contribution
113
+
114
+ * Everything needs to be tested! Well not everything. I really do not have an idea how to test Gui. But rest of the code must be tested before merging to master. If you do not now how to test it, than open new issue and we can discuss it.
115
+ * If something is hard to implement or change, then please refactor the code first so change is easy and then implement it.
116
+
117
+ [robocode]: http://robocode.sourceforge.net/
118
+ [rrobots]: http://rrobots.rubyforge.org/
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task default: :spec
data/bin/ruby_arena ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby_arena'
4
+
5
+ options = OptionParser.parse(ARGV)
6
+
7
+ RubyArena::Game.new({ options: options, robot_files: ARGV }).run
@@ -0,0 +1,68 @@
1
+ module RubyArena
2
+ class Ai
3
+ attr_reader :robot, :command_parser
4
+
5
+ def initialize(args)
6
+ @robot = args.fetch(:robot)
7
+ @command_parser = args.fetch(:command_parser)
8
+ end
9
+
10
+ def tick
11
+ raise NotImplementedError, 'Your robot should implement tick method'
12
+ end
13
+
14
+ private
15
+
16
+ def x
17
+ @robot.x
18
+ end
19
+
20
+ def y
21
+ @robot.y
22
+ end
23
+
24
+ def speed
25
+ @robot.speed
26
+ end
27
+
28
+ def heading
29
+ @robot.heading
30
+ end
31
+
32
+ def time
33
+ robot.time
34
+ end
35
+
36
+ def radar_view_angle
37
+ robot.radar_view_angle
38
+ end
39
+
40
+ def turn(angle)
41
+ command_parser.turn(angle)
42
+ end
43
+
44
+ def turn_gun(angle)
45
+ command_parser.turn_gun(angle)
46
+ end
47
+
48
+ def turn_radar(angle)
49
+ command_parser.turn_radar(angle)
50
+ end
51
+
52
+ def accelerate
53
+ command_parser.accelerate
54
+ end
55
+
56
+ def decelerate
57
+ command_parser.decelerate
58
+ end
59
+
60
+ def fire
61
+ command_parser.fire
62
+ end
63
+
64
+ def set_radar_view_angle(angle)
65
+ command_parser.radar_view_angle(angle)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ module RubyArena
2
+ class Arena
3
+ WIDTH = 800
4
+ HEIGHT = 600
5
+
6
+ attr_reader :robots, :bullets, :time, :total_robots_count
7
+
8
+ def initialize
9
+ @robots = []
10
+ @bullets = []
11
+ @time = 0
12
+ @total_robots_count = 0
13
+ end
14
+
15
+ def add_robot(robot)
16
+ @robots << robot
17
+ @total_robots_count += 1
18
+ end
19
+
20
+ def add_bullet(bullet)
21
+ @bullets << bullet
22
+ end
23
+
24
+ def update
25
+ send_tick_to_all_robots
26
+ send_update_to_all_robots
27
+ send_update_to_all_bullets
28
+ remove_dead_robots
29
+ remove_dead_bullets
30
+ update_time
31
+ end
32
+
33
+ def game_over?
34
+ robots_count <= 1 and total_robots_count > 1
35
+ end
36
+
37
+ def robots_count
38
+ robots.count
39
+ end
40
+
41
+ def width
42
+ WIDTH
43
+ end
44
+
45
+ def height
46
+ HEIGHT
47
+ end
48
+
49
+ private
50
+
51
+ def send_tick_to_all_robots
52
+ robots.each(&:tick)
53
+ end
54
+
55
+ def send_update_to_all_robots
56
+ robots.each(&:update)
57
+ end
58
+
59
+ def send_update_to_all_bullets
60
+ bullets.each(&:update)
61
+ end
62
+
63
+ def remove_dead_robots
64
+ robots.delete_if { |robot| robot.dead? }
65
+ end
66
+
67
+ def remove_dead_bullets
68
+ bullets.delete_if { |bullet| bullet.dead? }
69
+ end
70
+
71
+ def update_time
72
+ @time += 1
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,53 @@
1
+ module RubyArena
2
+ class Bullet
3
+ include Movable
4
+
5
+ DEFAULT_SPEED = 10
6
+ DEFAULT_ENERGY = 10
7
+ SIZE = 4
8
+
9
+ attr_reader :x, :y, :heading, :speed, :arena, :origin
10
+
11
+ def initialize(args)
12
+ @x = args.fetch(:x)
13
+ @y = args.fetch(:y)
14
+ @heading = args.fetch(:heading)
15
+ @speed = args.fetch(:speed, DEFAULT_SPEED)
16
+ @arena = args.fetch(:arena)
17
+ @origin = args.fetch(:origin)
18
+ @dead = false
19
+ end
20
+
21
+ def update
22
+ move
23
+ check_if_hit_some_robot
24
+ end
25
+
26
+ def intersect?(object)
27
+ Gosu.distance(x, y, object.x, object.y) < size/2 + object.size/2
28
+ end
29
+
30
+ def size
31
+ SIZE
32
+ end
33
+
34
+ def energy
35
+ DEFAULT_ENERGY
36
+ end
37
+
38
+ def dead?
39
+ @dead
40
+ end
41
+
42
+ private
43
+
44
+ def check_if_hit_some_robot
45
+ arena.robots.each do |robot|
46
+ if robot != origin && intersect?(robot)
47
+ robot.hit(self)
48
+ @dead = true
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ module RubyArena
2
+ class BulletRenderer
3
+ attr_reader :bullet, :window
4
+
5
+ def initialize(args)
6
+ @bullet = args.fetch(:bullet)
7
+ @window = args.fetch(:window)
8
+ end
9
+
10
+ def draw
11
+ window.draw_quad(
12
+ x1, y1, color,
13
+ x1, y2, color,
14
+ x2, y2, color,
15
+ x2, y1, color
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ def x1
22
+ x - size/2
23
+ end
24
+
25
+ def x2
26
+ x + size/2
27
+ end
28
+
29
+ def y1
30
+ y - size/2
31
+ end
32
+
33
+ def y2
34
+ y + size/2
35
+ end
36
+
37
+ def x
38
+ bullet.x
39
+ end
40
+
41
+ def y
42
+ bullet.y
43
+ end
44
+
45
+ def color
46
+ Gosu::Color::YELLOW
47
+ end
48
+
49
+ def size
50
+ bullet.size
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ module RubyArena
2
+ class ClassLoader
3
+ attr_reader :filename
4
+
5
+ def initialize(filename)
6
+ @filename = filename
7
+ require filename
8
+ end
9
+
10
+ def get_class
11
+ Object.const_get(class_name)
12
+ end
13
+
14
+ private
15
+
16
+ def class_name
17
+ File.basename(filename, '.rb').split('_').map(&:capitalize).join
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ module RubyArena
2
+ class CommandParser
3
+ attr_reader :actions
4
+
5
+ def initialize
6
+ reset_actions
7
+ end
8
+
9
+ def turn(angle)
10
+ @actions[:turn] = angle
11
+ end
12
+
13
+ def turn_gun(angle)
14
+ @actions[:turn_gun] = angle
15
+ end
16
+
17
+ def turn_radar(angle)
18
+ @actions[:turn_radar] = angle
19
+ end
20
+
21
+ def accelerate
22
+ @actions[:accelerate] = true
23
+ end
24
+
25
+ def decelerate
26
+ @actions[:decelerate] = true
27
+ end
28
+
29
+ def fire
30
+ @actions[:fire] = true
31
+ end
32
+
33
+ def radar_view_angle(angle)
34
+ @actions[:radar_view_angle] = angle
35
+ end
36
+
37
+ def reset_actions
38
+ @actions = {}
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ module RubyArena
2
+ class Game
3
+ attr_reader :arena, :level
4
+
5
+ def initialize(args)
6
+ @arena = Arena.new
7
+ @robot_files = args[:robot_files]
8
+ @level = Level.new(args[:options][:level]) if args[:options][:level]
9
+ add_robots_to_arena
10
+ end
11
+
12
+ def run
13
+ gui.show
14
+ end
15
+
16
+ def game_over?
17
+ arena.game_over?
18
+ end
19
+
20
+ private
21
+
22
+ def gui
23
+ Gui.new(arena)
24
+ end
25
+
26
+ def add_robots_to_arena
27
+ add_user_robots
28
+ add_cpu_robots if level
29
+ end
30
+
31
+ def add_user_robots
32
+ user_robots.each do |robot|
33
+ add_robot(robot)
34
+ end
35
+ end
36
+
37
+ def user_robots
38
+ @robot_files.map do |file|
39
+ ai_class = load_class_from_file(file)
40
+
41
+ Robot.new(options_for_user_robot(ai_class))
42
+ end
43
+ end
44
+
45
+ def options_for_user_robot(ai_class)
46
+ options = { arena: arena, ai: ai_class }
47
+ options.merge!(level.options_for_user_robot) if level
48
+ options
49
+ end
50
+
51
+ def add_cpu_robots
52
+ options_for_user_robots.each do |options|
53
+ options.merge!({ arena: arena })
54
+ add_robot(Robot.new(options))
55
+ end
56
+ end
57
+
58
+ def options_for_user_robots
59
+ level.options_for_cpu_robots
60
+ end
61
+
62
+ def add_robot(robot)
63
+ arena.add_robot(robot)
64
+ end
65
+
66
+ def load_class_from_file(file)
67
+ ClassLoader.new("#{pwd}/#{file}").get_class
68
+ end
69
+
70
+ def pwd
71
+ Dir.getwd
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ module RubyArena
2
+ class Gui < Gosu::Window
3
+ WIDTH = 800
4
+ HEIGHT = 600
5
+
6
+ attr_reader :arena, :robot_renderers
7
+
8
+ def initialize(arena)
9
+ @arena = arena
10
+ @_robot_renderers = {}
11
+ @_bullet_renderers = {}
12
+ super(width, height, false)
13
+ end
14
+
15
+ def update
16
+ arena.update
17
+ end
18
+
19
+ def draw
20
+ draw_robots
21
+ draw_bullets
22
+ end
23
+
24
+ private
25
+
26
+ def width
27
+ arena.width
28
+ end
29
+
30
+ def height
31
+ arena.height
32
+ end
33
+
34
+ def draw_robots
35
+ robots.each do |robot|
36
+ robot_renderer(robot).draw
37
+ end
38
+ end
39
+
40
+ def draw_bullets
41
+ bullets.each do |bullet|
42
+ bullet_renderer(bullet).draw
43
+ end
44
+ end
45
+
46
+ def robot_renderer(robot)
47
+ @_robot_renderers[robot] ||= RobotRenderer.new(window: self, robot: robot)
48
+ end
49
+
50
+ def bullet_renderer(bullet)
51
+ @_bullet_renderers[bullet] ||= BulletRenderer.new(window: self, bullet: bullet)
52
+ end
53
+
54
+ def robots
55
+ arena.robots
56
+ end
57
+
58
+ def bullets
59
+ arena.bullets
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,42 @@
1
+ module RubyArena
2
+ class Level
3
+ LEVELS = {
4
+ 0 => {
5
+ robots: [
6
+ { ai: Level0, x: 600, y: 300 }
7
+ ],
8
+ user_robot_options: { x: 200, y: 300 }
9
+ },
10
+ 1 => {
11
+ robots: [
12
+ { ai: Level1, x: 600, y: 300 }
13
+ ],
14
+ user_robot_options: { x: 200, y: 300 }
15
+ }
16
+ }
17
+
18
+ attr_reader :level
19
+
20
+ def initialize(level)
21
+ @level = level
22
+ exit_if_level_is_not_defined
23
+ end
24
+
25
+ def options_for_cpu_robots
26
+ LEVELS[level][:robots]
27
+ end
28
+
29
+ def options_for_user_robot
30
+ LEVELS[level][:user_robot_options]
31
+ end
32
+
33
+ private
34
+
35
+ def exit_if_level_is_not_defined
36
+ if LEVELS[level].nil?
37
+ puts "Level is not defined"
38
+ exit
39
+ end
40
+ end
41
+ end
42
+ end