rsokoban 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +46 -0
- data/LICENSE.txt +20 -0
- data/README.md +47 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/bin/rsokoban +35 -0
- data/levels/default.xml +771 -0
- data/lib/level.rb +68 -0
- data/lib/rsokoban_game.rb +140 -0
- data/rsokoban.gemspec +77 -0
- data/spec/rsokoban_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +155 -0
data/lib/level.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'happymapper'
|
2
|
+
|
3
|
+
module RSokoban
|
4
|
+
attr_accessor :levels
|
5
|
+
|
6
|
+
class LevelData
|
7
|
+
include HappyMapper
|
8
|
+
|
9
|
+
tag 'Level'
|
10
|
+
element :id, String
|
11
|
+
element :copyright, String
|
12
|
+
element :width, Integer
|
13
|
+
element :height, Integer
|
14
|
+
has_many :lines, String, :tag => 'L'
|
15
|
+
end
|
16
|
+
|
17
|
+
class Level
|
18
|
+
attr_accessor :walls
|
19
|
+
attr_accessor :boxes
|
20
|
+
attr_accessor :player
|
21
|
+
attr_accessor :goals
|
22
|
+
|
23
|
+
def initialize(data)
|
24
|
+
@walls = {}
|
25
|
+
@boxes = {}
|
26
|
+
@player = {}
|
27
|
+
@goals = {}
|
28
|
+
|
29
|
+
data.lines.each_with_index do |line, row|
|
30
|
+
line.split(//).each_with_index do |char, col|
|
31
|
+
case char
|
32
|
+
when '#'
|
33
|
+
@walls[[row, col]] = true
|
34
|
+
when '$'
|
35
|
+
@boxes[[row, col]] = true
|
36
|
+
when '.'
|
37
|
+
@goals[[row, col]] = true
|
38
|
+
when '@'
|
39
|
+
@player = {row: row, col: col}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def draw(window)
|
46
|
+
@walls.keys.each do |k|
|
47
|
+
window.move k[0], k[1]
|
48
|
+
window.addstr '#'
|
49
|
+
end
|
50
|
+
|
51
|
+
@goals.keys.each do |k|
|
52
|
+
window.move k[0], k[1]
|
53
|
+
window.addstr '.'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.parse(string)
|
58
|
+
LevelData.parse(string).map do |level|
|
59
|
+
Level.new level
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.load(filename)
|
64
|
+
self.parse(File.open(filename).read)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module RSokoban
|
2
|
+
class RSokobanGame
|
3
|
+
# Add these to [@player[:row], @player[:col]] to move the player
|
4
|
+
# in the indicated direction.
|
5
|
+
DIRECTIONS = {
|
6
|
+
left: [ 0, -1],
|
7
|
+
right: [ 0, 1],
|
8
|
+
up: [-1, 0],
|
9
|
+
down: [ 1, 0]
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(window)
|
13
|
+
@window = window
|
14
|
+
end
|
15
|
+
|
16
|
+
def can_move?(direction)
|
17
|
+
# Calculate the player's new position if this move is to be executed. Remember that
|
18
|
+
# `new_player` is an Array whereas @player is a Hash.
|
19
|
+
new_player = [@player[:row], @player[:col]].zip(DIRECTIONS[direction]).map{|a, b| a + b}
|
20
|
+
|
21
|
+
if @walls[new_player] then
|
22
|
+
return false
|
23
|
+
else
|
24
|
+
if @boxes[new_player] then
|
25
|
+
# Calculate the position of any box that's in the player's way.
|
26
|
+
new_box = new_player.zip(DIRECTIONS[direction]).map{|a, b| a + b}
|
27
|
+
if @walls[new_box] or @boxes[new_box] then
|
28
|
+
return false
|
29
|
+
else
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
else
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def draw
|
39
|
+
@current_level.walls.keys.each do |k|
|
40
|
+
@window.move k[0], k[1]
|
41
|
+
@window.addstr '#'
|
42
|
+
end
|
43
|
+
@goals.keys.each do |k|
|
44
|
+
@window.move k[0], k[1]
|
45
|
+
@window.addstr '.'
|
46
|
+
end
|
47
|
+
@boxes.keys.each do |k|
|
48
|
+
@window.move k[0], k[1]
|
49
|
+
if @goals[k] then
|
50
|
+
@window.addstr '+'
|
51
|
+
else
|
52
|
+
@window.addstr '$'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@window.move @player[:row], @player[:col]
|
56
|
+
@window.addstr '@'
|
57
|
+
@window.refresh
|
58
|
+
end
|
59
|
+
|
60
|
+
def level_complete?
|
61
|
+
(@boxes.keys - @goals.keys).empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def move(direction, undoing = false)
|
65
|
+
return unless can_move?(direction)
|
66
|
+
|
67
|
+
new_player = [@player[:row], @player[:col]].zip(DIRECTIONS[direction]).map{|a, b| a + b}
|
68
|
+
if @boxes[new_player] then
|
69
|
+
new_box = new_player.zip(DIRECTIONS[direction]).map{|a, b| a + b}
|
70
|
+
@boxes[new_box] = true
|
71
|
+
@boxes.delete new_player
|
72
|
+
end
|
73
|
+
|
74
|
+
# Cover up the space behind the player with a space.
|
75
|
+
@window.move @player[:row], @player[:col]
|
76
|
+
@window.addstr ' '
|
77
|
+
|
78
|
+
@player[:row], @player[:col] = new_player
|
79
|
+
@moves.push direction unless undoing
|
80
|
+
end
|
81
|
+
|
82
|
+
def play_level(level)
|
83
|
+
@current_level = level.dup
|
84
|
+
|
85
|
+
@walls = level.walls.dup
|
86
|
+
@boxes = level.boxes.dup
|
87
|
+
@player = level.player.dup
|
88
|
+
@goals = level.goals.dup
|
89
|
+
|
90
|
+
@moves = []
|
91
|
+
|
92
|
+
draw
|
93
|
+
|
94
|
+
until level_complete?
|
95
|
+
ch = @window.getch
|
96
|
+
|
97
|
+
case ch
|
98
|
+
when 'q'.ord
|
99
|
+
exit
|
100
|
+
when 'r'.ord
|
101
|
+
@window.clear
|
102
|
+
return play_level(level)
|
103
|
+
when 'n'.ord
|
104
|
+
return
|
105
|
+
when 'z'.ord
|
106
|
+
undo
|
107
|
+
when Ncurses::KEY_DOWN, 's'.ord
|
108
|
+
move :down
|
109
|
+
when Ncurses::KEY_UP, 'w'.ord
|
110
|
+
move :up
|
111
|
+
when Ncurses::KEY_RIGHT, 'd'.ord
|
112
|
+
move :right
|
113
|
+
when Ncurses::KEY_LEFT, 'a'.ord
|
114
|
+
move :left
|
115
|
+
end
|
116
|
+
|
117
|
+
draw
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def undo
|
123
|
+
return if @moves.empty?
|
124
|
+
|
125
|
+
direction = case @moves.pop
|
126
|
+
when :left
|
127
|
+
:right
|
128
|
+
when :right
|
129
|
+
:left
|
130
|
+
when :up
|
131
|
+
:down
|
132
|
+
when :down
|
133
|
+
:up
|
134
|
+
end
|
135
|
+
|
136
|
+
move direction, true
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
data/rsokoban.gemspec
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "rsokoban"
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Max Thom Stahl"]
|
12
|
+
s.date = "2012-05-18"
|
13
|
+
s.description = "A Sokoban clone written in Ruby using the ncurses library."
|
14
|
+
s.email = "max@dangerouscuteness.com"
|
15
|
+
s.executables = ["rsokoban"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".rspec",
|
23
|
+
".rvmrc",
|
24
|
+
"Gemfile",
|
25
|
+
"Gemfile.lock",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.md",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"bin/rsokoban",
|
31
|
+
"levels/default.xml",
|
32
|
+
"lib/level.rb",
|
33
|
+
"lib/rsokoban_game.rb",
|
34
|
+
"rsokoban.gemspec",
|
35
|
+
"spec/rsokoban_spec.rb",
|
36
|
+
"spec/spec_helper.rb"
|
37
|
+
]
|
38
|
+
s.homepage = "http://github.com/mstahl/rsokoban"
|
39
|
+
s.licenses = ["MIT"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = "1.8.17"
|
42
|
+
s.summary = "Sokoban, written in Ruby."
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_runtime_dependency(%q<happymapper>, [">= 0"])
|
49
|
+
s.add_runtime_dependency(%q<ncurses-ruby>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
58
|
+
s.add_dependency(%q<ncurses-ruby>, [">= 0"])
|
59
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
60
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
61
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
62
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
63
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
64
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
68
|
+
s.add_dependency(%q<ncurses-ruby>, [">= 0"])
|
69
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
71
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
72
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
74
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'rsokoban'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rsokoban
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Max Thom Stahl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: happymapper
|
16
|
+
requirement: &70276082784920 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70276082784920
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ncurses-ruby
|
27
|
+
requirement: &70276082783860 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70276082783860
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: awesome_print
|
38
|
+
requirement: &70276082783120 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70276082783120
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &70276082782300 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70276082782300
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yard
|
60
|
+
requirement: &70276082781620 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70276082781620
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: &70276082780460 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70276082780460
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: jeweler
|
82
|
+
requirement: &70276082779740 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70276082779740
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: simplecov
|
93
|
+
requirement: &70276082778940 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70276082778940
|
102
|
+
description: A Sokoban clone written in Ruby using the ncurses library.
|
103
|
+
email: max@dangerouscuteness.com
|
104
|
+
executables:
|
105
|
+
- rsokoban
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files:
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
files:
|
111
|
+
- .document
|
112
|
+
- .rspec
|
113
|
+
- .rvmrc
|
114
|
+
- Gemfile
|
115
|
+
- Gemfile.lock
|
116
|
+
- LICENSE.txt
|
117
|
+
- README.md
|
118
|
+
- Rakefile
|
119
|
+
- VERSION
|
120
|
+
- bin/rsokoban
|
121
|
+
- levels/default.xml
|
122
|
+
- lib/level.rb
|
123
|
+
- lib/rsokoban_game.rb
|
124
|
+
- rsokoban.gemspec
|
125
|
+
- spec/rsokoban_spec.rb
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
homepage: http://github.com/mstahl/rsokoban
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ! '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
hash: -1397569013250805758
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 1.8.17
|
152
|
+
signing_key:
|
153
|
+
specification_version: 3
|
154
|
+
summary: Sokoban, written in Ruby.
|
155
|
+
test_files: []
|