rsokoban 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Rsokoban" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -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: []