scuby-wars 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +48 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/example_bots/Gemfile +2 -0
- data/example_bots/README.mdown +7 -0
- data/example_bots/chaos.rb +43 -0
- data/example_bots/shot_and_run.rb +12 -0
- data/lib/scruby_wars.rb +10 -0
- data/lib/scuby-wars.rb +1 -0
- data/lib/scuby_wars/client.rb +135 -0
- data/lib/scuby_wars/monitor.rb +9 -0
- data/node-server/express_server.js +89 -0
- data/node-server/lib/grasshopper.js +261 -0
- data/node-server/public/explosion.png +0 -0
- data/node-server/public/monster.js +114 -0
- data/node-server/public/panzer.png +0 -0
- data/node-server/public/raphael.min.js +7 -0
- data/node-server/views/index.ejs +129 -0
- data/scuby-wars.gemspec +71 -0
- metadata +141 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Galaxy Cats IT Consulting
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= scuby-wars
|
2
|
+
|
3
|
+
Scuby-Wars is a little game where you have to implement your own AI-bots who will fight on a server against each other.
|
4
|
+
|
5
|
+
== How to write your own Bot
|
6
|
+
|
7
|
+
You have to implement a class which inherits from ScubyWars::Client
|
8
|
+
|
9
|
+
class AndisBot < ScubyWars::Client
|
10
|
+
end
|
11
|
+
|
12
|
+
now you have to implement on method called think
|
13
|
+
|
14
|
+
class AndisBot < ScubyWars::Client
|
15
|
+
|
16
|
+
def think
|
17
|
+
# do your intelligent stuff here
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
in your instance you can access the attribute world which has all informations about the world. And there are several methods you can call to control your bot:
|
23
|
+
|
24
|
+
run
|
25
|
+
stop
|
26
|
+
fire
|
27
|
+
cease_fire
|
28
|
+
turn_left
|
29
|
+
turn_right
|
30
|
+
stop_turning
|
31
|
+
|
32
|
+
== Connect your bot
|
33
|
+
|
34
|
+
YourBot.new("server.address:port", "YourName").join()
|
35
|
+
|
36
|
+
== Note on Patches/Pull Requests
|
37
|
+
|
38
|
+
* Fork the project.
|
39
|
+
* Make your feature addition or bug fix.
|
40
|
+
* Add tests for it. This is important so I don't break it in a
|
41
|
+
future version unintentionally.
|
42
|
+
* Commit, do not mess with rakefile, version, or history.
|
43
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
44
|
+
* Send me a pull request. Bonus points for topic branches.
|
45
|
+
|
46
|
+
== Copyright
|
47
|
+
|
48
|
+
Copyright (c) 2011 Andi Bade. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "scuby-wars"
|
8
|
+
gem.summary = %Q{Library for programming little AI-Bots, fighting against each other}
|
9
|
+
gem.description = %Q{Library for programming little AI-Bots, fighting against each other}
|
10
|
+
gem.email = "andi@galaxycats.com"
|
11
|
+
gem.homepage = "http://github.com/galaxycats/scuby-wars"
|
12
|
+
gem.authors = ["Andi Bade", "Dirk Breuer", "Sebastian Cohnen"]
|
13
|
+
gem.add_dependency "yajl-ruby", ">= 0.7.8"
|
14
|
+
gem.add_dependency "httparty", ">= 0.6.1"
|
15
|
+
gem.add_dependency "i18n", ">= 0.5.0"
|
16
|
+
gem.add_dependency "activesupport", ">= 3.0.3"
|
17
|
+
# gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
18
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:test) do |test|
|
27
|
+
test.libs << 'lib' << 'test'
|
28
|
+
test.pattern = 'test/**/test_*.rb'
|
29
|
+
test.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/test_*.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :test => :check_dependencies
|
46
|
+
|
47
|
+
task :default => :test
|
48
|
+
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
Rake::RDocTask.new do |rdoc|
|
51
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "scuby-wars #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "scuby-wars"
|
2
|
+
|
3
|
+
class Chaos < ScubyWars::Client
|
4
|
+
|
5
|
+
def think
|
6
|
+
change_turning
|
7
|
+
change_fireing
|
8
|
+
change_running
|
9
|
+
end
|
10
|
+
|
11
|
+
def change_turning
|
12
|
+
if self.turning?
|
13
|
+
self.stop_turning if rand(2) == 1
|
14
|
+
else
|
15
|
+
case rand(4)
|
16
|
+
when 0
|
17
|
+
self.turn_left
|
18
|
+
when 1
|
19
|
+
self.turn_right
|
20
|
+
else
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def change_fireing
|
26
|
+
if self.fireing?
|
27
|
+
self.stop_fireing if rand(4) == 1
|
28
|
+
else
|
29
|
+
self.fire if rand(4) == 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def change_running
|
34
|
+
if self.running?
|
35
|
+
self.stop if rand(2) == 0
|
36
|
+
else
|
37
|
+
self.run if rand(4) < 4
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Chaos.new("localhost:3000", "Chaos").join()
|
data/lib/scruby_wars.rb
ADDED
data/lib/scuby-wars.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "scruby_wars"
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module ScubyWars
|
2
|
+
class Client
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
attr_accessor :world, :server
|
6
|
+
|
7
|
+
##
|
8
|
+
# Example:
|
9
|
+
# player = ScubyWars::Client.join("localhost:3000", "player-name")
|
10
|
+
# player.stop!
|
11
|
+
# player.turn_left!
|
12
|
+
# player.run!
|
13
|
+
# player.fire!
|
14
|
+
def self.join(server, nick)
|
15
|
+
game = new(server, nick)
|
16
|
+
if block_given?
|
17
|
+
Yajl::HttpStream.get(URI.parse("http://#{server}/world")) do |world|
|
18
|
+
game.world = world
|
19
|
+
game.instance_eval(yield)
|
20
|
+
game.update_player
|
21
|
+
end
|
22
|
+
end
|
23
|
+
game
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(server, nick)
|
27
|
+
@player = {:nick => nick, :actions => {:thrust => false, :turnLeft => false, :turnRight => false, :fire => false}}
|
28
|
+
@server = server
|
29
|
+
begin
|
30
|
+
self.class.post("http://#{@server}/players/#{@player[:nick]}")
|
31
|
+
rescue => e
|
32
|
+
puts "Could not connect to game-server"
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def join
|
38
|
+
Yajl::HttpStream.get(URI.parse("http://#{server}/world")) do |world|
|
39
|
+
self.world = world
|
40
|
+
think
|
41
|
+
update_player
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
@player[:actions][:thrust] = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def running?
|
50
|
+
@player[:actions][:thrust]
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
@player[:actions][:thrust] = false
|
55
|
+
end
|
56
|
+
|
57
|
+
def turn_left
|
58
|
+
@player[:actions][:turnLeft] = true
|
59
|
+
@player[:actions][:turnRight] = false
|
60
|
+
end
|
61
|
+
|
62
|
+
def turning_left?
|
63
|
+
@player[:actions][:turnLeft]
|
64
|
+
end
|
65
|
+
|
66
|
+
def turn_right
|
67
|
+
@player[:actions][:turnLeft] = false
|
68
|
+
@player[:actions][:turnRight] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def turning_right?
|
72
|
+
@player[:actions][:turnRight]
|
73
|
+
end
|
74
|
+
|
75
|
+
def turning?
|
76
|
+
turning_left? || turning_right?
|
77
|
+
end
|
78
|
+
|
79
|
+
def stop_turning
|
80
|
+
@player[:actions][:turnLeft] = false
|
81
|
+
@player[:actions][:turnRight] = false
|
82
|
+
end
|
83
|
+
|
84
|
+
def fire
|
85
|
+
@player[:actions][:fire] = true
|
86
|
+
end
|
87
|
+
|
88
|
+
def fireing?
|
89
|
+
@player[:actions][:fire]
|
90
|
+
end
|
91
|
+
|
92
|
+
def cease_fire
|
93
|
+
@player[:actions][:fire] = false
|
94
|
+
end
|
95
|
+
|
96
|
+
def stop_fireing
|
97
|
+
cease_fire
|
98
|
+
end
|
99
|
+
|
100
|
+
%w(run stop turn_left turn_right stop_turning fire cease_fire).each do |meth_name|
|
101
|
+
define_method "#{meth_name}!" do
|
102
|
+
send(meth_name)
|
103
|
+
update_player
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_player
|
108
|
+
puts "Sending #{@player.inspect} to http://#{@server}/players/#{@player[:nick]}"
|
109
|
+
self.class.put("http://#{@server}/players/#{@player[:nick]}", :body => Yajl::Encoder.encode(@player))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
# require "grashopper"
|
117
|
+
|
118
|
+
# Grasshopper.join("localhost:3000", "andi1") do
|
119
|
+
# world
|
120
|
+
#
|
121
|
+
# run
|
122
|
+
# # stop!
|
123
|
+
# #
|
124
|
+
# turn_left
|
125
|
+
# # turn_right!
|
126
|
+
# # stop_turning!
|
127
|
+
# #
|
128
|
+
# # shoot!
|
129
|
+
# # cease_fire!
|
130
|
+
# end
|
131
|
+
|
132
|
+
# p = Grasshopper.join("localhost:3000", "andi2")
|
133
|
+
# p.run!
|
134
|
+
# p.stop!
|
135
|
+
# p.turn_right!
|
@@ -0,0 +1,89 @@
|
|
1
|
+
var app = require('express').createServer(),
|
2
|
+
io = require('socket.io'), // for npm, otherwise use require('./path/to/socket.io')
|
3
|
+
sys = require('sys'),
|
4
|
+
express = require('express');
|
5
|
+
grasshopper = require('./lib/grasshopper');
|
6
|
+
|
7
|
+
var game = grasshopper.getGame();
|
8
|
+
var socket = io.listen(app);
|
9
|
+
var knownClients = [];
|
10
|
+
|
11
|
+
app.configure(function() {
|
12
|
+
app.use(app.router);
|
13
|
+
app.use(express.staticProvider(__dirname + '/public'));
|
14
|
+
app.set("views", __dirname + "/views");
|
15
|
+
app.set('view engine', 'ejs');
|
16
|
+
});
|
17
|
+
|
18
|
+
app.get("/", function(req, res) {
|
19
|
+
res.render("index", { "layout" : false });
|
20
|
+
});
|
21
|
+
|
22
|
+
app.get("/world", function(req, res) {
|
23
|
+
res.writeHead(200, { "Content-Type" : "application/json"});
|
24
|
+
sys.puts("Registered client to consume world data.");
|
25
|
+
knownClients.push(res);
|
26
|
+
});
|
27
|
+
|
28
|
+
app.post("/players/:name", function(req, res) {
|
29
|
+
try {
|
30
|
+
var player = game.addPlayer({ip: req.headers.ip, name: req.params.name})
|
31
|
+
sys.puts("Added player: " + player.name);
|
32
|
+
res.writeHead(200, { "Content-Type" : "application/json"});
|
33
|
+
res.write(JSON.stringify({ "ok" : true }));
|
34
|
+
} catch(m) {
|
35
|
+
sys.puts(m);
|
36
|
+
res.writeHead(409, { "Content-Type" : "application/json"});
|
37
|
+
res.write(JSON.stringify({ "error" : m }));
|
38
|
+
}
|
39
|
+
res.end();
|
40
|
+
});
|
41
|
+
|
42
|
+
app.put("/players/:name", function(req, res) {
|
43
|
+
var player = game.findPlayer(req.params.name);
|
44
|
+
|
45
|
+
if (player) {
|
46
|
+
var body = "";
|
47
|
+
req.on("data", function(data) {
|
48
|
+
body += data;
|
49
|
+
});
|
50
|
+
|
51
|
+
req.on("end", function() {
|
52
|
+
actions = JSON.parse(body).actions;
|
53
|
+
player.newActions(actions);
|
54
|
+
res.writeHead(200, { "Content-Type" : "application/json"});
|
55
|
+
res.write(JSON.stringify({ "ok" : true }));
|
56
|
+
res.end();
|
57
|
+
});
|
58
|
+
} else {
|
59
|
+
console.log("Player with name '" + req.params.name + "' couldn't be found.");
|
60
|
+
res.writeHead(404, { "Content-Type" : "application/json"});
|
61
|
+
res.write(JSON.stringify({ "error" : "player not found: " + req.params.name }));
|
62
|
+
res.end();
|
63
|
+
};
|
64
|
+
});
|
65
|
+
|
66
|
+
app.post("/reset", function(req, res) {
|
67
|
+
game = grasshopper.resetGame();
|
68
|
+
res.writeHead(200, { "Content-Type" : "application/json" });
|
69
|
+
res.write(JSON.stringify({ "ok" : true }));
|
70
|
+
res.end();
|
71
|
+
});
|
72
|
+
|
73
|
+
app.listen(3000);
|
74
|
+
|
75
|
+
socket.on('connection', function(client) {
|
76
|
+
sys.puts("A new spectator just connected. Welcome it!")
|
77
|
+
|
78
|
+
client.on('message', function(message) {});
|
79
|
+
client.on('disconnect', function() {});
|
80
|
+
});
|
81
|
+
|
82
|
+
setInterval(function() {
|
83
|
+
game.turn();
|
84
|
+
socket.broadcast(game.world);
|
85
|
+
var jsonWorld = JSON.stringify(game.world);
|
86
|
+
knownClients.forEach(function(res) {
|
87
|
+
res.write(jsonWorld);
|
88
|
+
});
|
89
|
+
}, grasshopper.WorldDefs.serverTick);
|