bangkok 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ 2005-03-24 Jim Menard <jimm@io.com>
2
+
3
+ * Initial project creation.
4
+
5
+
data/Credits ADDED
@@ -0,0 +1,6 @@
1
+ Bangkok is developed by Jim Menard <jimm@io.com> and originated from ideas and
2
+ conversations with Tom Peak <himself@tompeak.com>.
3
+
4
+ Additional bug fixes and suggestions have come from:
5
+
6
+ [nobody yet, since this is the first release.]
data/README ADDED
@@ -0,0 +1,227 @@
1
+ = Bangkok
2
+
3
+ Bangkok reads chess game descriptions and re-play the games. Notice of events
4
+ (moves, captures, checks, etc.) are sent to a listener. Bangkok comes with a
5
+ listener that generates a MIDI file. In other words, the chess game is turned
6
+ into music.
7
+
8
+ Bangkok originated as the code for an art project by Tom Peak
9
+ <himself@tompeak.com>.
10
+
11
+ The Web site of Bangkok is (http://bangkok.rubyforge.org). The RubyForge
12
+ project page is http://rubyforge.org/projects/bangkok, where the latest
13
+ version of bangkok may be downloaded. bangkok is also available as a RubyGem.
14
+
15
+
16
+ === Recent Changes
17
+
18
+ Since this is the first release, there's nothing to say.
19
+
20
+
21
+ == Dependencies
22
+
23
+ The MIDI generation portion of Bangkok requires midilib 0.8.4 or later. If you
24
+ install Bangkok as a RubyGem, it will fetch and install midilib for you.
25
+
26
+ The test suite in the tests directory requires the testing framework TestUnit,
27
+ which comes with Ruby 1.8 and later and can also be found in the Ruby
28
+ Application Archive (http://raa.ruby-lang.org).
29
+
30
+ To rebuild the gem or RDocs or run the tests easily, you can use the Rakefile
31
+ which requires Rake (http://rake.rubyforge.org).
32
+
33
+
34
+ == Installation
35
+
36
+ === RubyGems Installation
37
+
38
+ To install Bangkok as a gem, type
39
+
40
+ % gem install bangkok
41
+
42
+ You may need root privileges to install the gem.
43
+
44
+ === Manual Installation
45
+
46
+ After downloading and expanding the archive, you can install Bangkok with the
47
+ command
48
+
49
+ % ruby install.rb
50
+ (or)
51
+ % ruby install.rb --install-dir=my_directory
52
+
53
+ You may need root privileges to install Bangkok.
54
+
55
+
56
+ == Testing
57
+
58
+ % rake test
59
+
60
+ runs all of the tests in the test directory.
61
+
62
+
63
+ == Overview
64
+
65
+ Bangkok replays a chess game. It reads chess game .pgn files. Interesting
66
+ events during each game (moves, captures, checks, etc.) are sent to a
67
+ listener.
68
+
69
+ Bangkok comes with a GameListener that creates a MIDI sequence. The listener
70
+ is given to a ChessGame object, which then reads a .pgn file, creates a board,
71
+ gives the moves to the board so it can move the pieces, and tells the listener
72
+ when the game is over. At that time, the GameListener writes the MIDI sequence
73
+ out to a MIDI file.
74
+
75
+
76
+ == How to Use
77
+
78
+ === From the command line
79
+
80
+ Here is how to generate a MIDI file from a chess match file, using the
81
+ built-in GameListener. (This command line is awkward, and needs to get
82
+ simpler.)
83
+
84
+ % bangkok [-c my_program_changes.rb] chess_game_file.pgn
85
+
86
+ Running that command creates the MIDI file chess_game_file.mid. The optional
87
+ configuration file my_program_changes.rb lets you change the default program
88
+ numbers used for each of the pieces. See examples/program_changes.rb for an
89
+ example file.
90
+
91
+ For example, to run bangkok on the chess game in the examples directory, type
92
+
93
+ % bangkok examples/game.pgn
94
+
95
+ === Example Scripts and Files
96
+
97
+ Here are short descriptions of each of the files found in the examples
98
+ directory.
99
+
100
+ * examples/announcer.rb creates a new listener that "announces" the game by
101
+ printing some descriptive text for everything that happens during the game.
102
+
103
+ * examples/game.pgn is an example chess match.
104
+
105
+ * examples/program_changes.rb is an example configuration file that shows you
106
+ how to change the default program change values. The values in this file are
107
+ the same as the default values used by GameListener.
108
+
109
+
110
+ == The Code
111
+
112
+ Only one class has anything to do with MIDI at all: GameListener. The rest of
113
+ the classes in Bangkok know only about chess moves. This separation allows you
114
+ to use Bangkok for your own nefarious chess-related purposes, whether musical
115
+ or un-.
116
+
117
+ GameListener creates a MIDI sequence from the chess game's moves. When a piece
118
+ moves, MIDI notes are generated and a few controller values are set. Any
119
+ listener you write must implement the methods
120
+ * start_game(io)
121
+ * end_game
122
+ * move(piece, from_square, to_square)
123
+ * capture(attacker_piece, loser_piece)
124
+ * check
125
+ * checkmate
126
+ * pawn_to_queen(pawn)
127
+
128
+ For each move, the GameListener generates three sets of MIDI events: one for
129
+ the move start, one for a point halfway between the start and the destination,
130
+ and one for the destination. GameListener#midi_for_position creates a pan
131
+ value (left = queenside, right = kingside), a volume value (max = center of
132
+ board, min = top and bottom of board), and a quarter note. The note is from a
133
+ C scale, where the low C = the color's home rank and the high C = the opposite
134
+ color's home rank.
135
+
136
+ SPEAKER midi 1 - 8 SPEAKER (black)
137
+
138
+ low
139
+ ^
140
+ |
141
+ high
142
+ volume
143
+ |
144
+ v
145
+ low
146
+
147
+ <---- panning ---->
148
+
149
+ SPEAKER midi 9 - 16 SPEAKER (white)
150
+
151
+ ChessGame takes a listener in its constructor and creates a Board. The method
152
+ ChessGame#read_moves reads game data. Calling ChessGame#play moves the pieces,
153
+ which in turn sends messages to the listener so it can react. See bin/bangkok
154
+ and examples/announcer.rb for examples of how to use a ChessGame object.
155
+
156
+ Board creates pieces and hands moves to pieces. It tells the listener about
157
+ things like captures and castles.
158
+
159
+ A Square represents a location on a Board.
160
+
161
+ A Piece has a color and a Square. It tells the listener about its moves.
162
+ Subclasses of Piece such as Queen and Knight override
163
+ Piece#could_perform_move, which checks to see if that piece could move from
164
+ its current position to the move's destination square. could_perform_move is
165
+ called by the Board to determine which piece on the board needs to be moved.
166
+
167
+ A Move has a color, a destination square, an optional starting rank or file,
168
+ and optional modifiers such as check or capture. A castle Move has no
169
+ destination square (the Board moves the pieces).
170
+
171
+
172
+ == Resources
173
+
174
+ The Ruby Web site (http://www.ruby-lang.org/en/index.html) contains an
175
+ introduction to Ruby, the Ruby Application Archive (RAA) at
176
+ http://raa.ruby-lang.org, and pointers to more information.
177
+
178
+
179
+ _Programming Ruby, The Pragmatic Programmer's Guide_, by David Thomas
180
+ and Andrew Hunt, is a well-written and practical introduction to Ruby. Its Web
181
+ page at http://www.rubycentral.com/book also contains a wealth of Ruby
182
+ information. Though the book is available online, I encourage you to purchase
183
+ a copy of the second edition, available at
184
+ http://pragmaticprogrammer.com/titles/ruby/.
185
+
186
+ midilib's home is http://midilib.rubyforge.org.
187
+
188
+
189
+ = To Do
190
+
191
+ :include: TODO
192
+
193
+
194
+ = Support
195
+
196
+ * Visit the forums, bug list, and mailing list pages at
197
+ http://rubyforge.org/projects/bangkok
198
+
199
+ * Send email to Jim Menard at mailto:jimm@io.com
200
+
201
+ * Ask on the ruby-talk mailing list
202
+
203
+
204
+ = Administrivia
205
+
206
+ Author:: Jim Menard (mailto:jimm@io.com)
207
+ Copyright:: Copyright (c) 2005 Jim Menard
208
+ License:: Distributed under the same license as Ruby.
209
+
210
+
211
+ == Copying
212
+
213
+ Bangkok is copyrighted free software by Jim Menard and is released under the
214
+ same license as Ruby. See the Ruby license at
215
+ http://www.ruby-lang.org/en/LICENSE.txt.
216
+
217
+ Bangkok may be freely copied in its entirety providing this notice, all
218
+ source code, all documentation, and all other files are included.
219
+
220
+ Bangkok is Copyright (c) 2005 by Jim Menard.
221
+
222
+
223
+ == Warranty
224
+
225
+ This software is provided "as is" and without any express or implied
226
+ warranties, including, without limitation, the implied warranties of
227
+ merchantability and fitness for a particular purpose.
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+ require_gem 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/contrib/rubyforgepublisher'
6
+ require 'rake/runtest'
7
+
8
+ PROJECT_NAME = 'bangkok'
9
+ RUBYFORGE_USER = 'jimm'
10
+ RDOC_DIR = 'html'
11
+
12
+ PKG_FILES = FileList[ 'ChangeLog', 'Credits', 'README', 'Rakefile', 'TODO',
13
+ 'examples/**/*',
14
+ 'html/**/*',
15
+ 'install.rb',
16
+ 'lib/**/*.rb',
17
+ 'test/**/*.rb']
18
+
19
+ task :default => [:package]
20
+
21
+ spec = Gem::Specification.new do |s|
22
+ s.platform = Gem::Platform::RUBY
23
+ s.name = PROJECT_NAME
24
+ s.version = `ruby -Ilib -e 'require "bangkok/info"; puts Version'`.strip
25
+ s.requirements << 'midilib'
26
+ s.add_dependency('midilib', '>= 0.8.4')
27
+
28
+ s.require_path = 'lib'
29
+ s.autorequire = PROJECT_NAME
30
+
31
+ s.files = PKG_FILES.to_a
32
+ s.executables = ['bangkok']
33
+ s.bindir = 'bin'
34
+
35
+ s.has_rdoc = true
36
+ s.rdoc_options << '--main' << 'README'
37
+ s.extra_rdoc_files = ['README', 'TODO']
38
+
39
+ s.author = 'Jim Menard'
40
+ s.email = 'jimm@io.com'
41
+ s.homepage = 'http://bangkok.rubyforge.org'
42
+ s.rubyforge_project = PROJECT_NAME
43
+
44
+ s.summary = "Chess game file reader and player; can turn games into MIDI files"
45
+ s.description = <<EOF
46
+ Bangkok can read chess game descriptions and re-play the games. Notice of
47
+ events (moves, captures, checks, etc.) are sent to a listener. Bangkok comes
48
+ with a listener that generates a MIDI file. In other words, the chess game is
49
+ turned into music.
50
+ EOF
51
+ end
52
+
53
+ # Creates a :package task (also named :gem). Also useful are :clobber_package
54
+ # and :repackage.
55
+ Rake::GemPackageTask.new(spec) do |pkg|
56
+ pkg.need_zip = true
57
+ pkg.need_tar = true
58
+ end
59
+
60
+ # require 'rbconfig'
61
+ # if Config::CONFIG['target_os'] =~ /mswin/i
62
+ # RDOC_FILES = FileList['README', 'TODO', 'lib/**/*.rb']
63
+ # RDOC_FILES.each { | f | file f }
64
+ # task :rdoc => RDOC_FILES do
65
+ # ruby "#{File.join(Config::CONFIG['bindir'], 'rdoc')} -o html " +
66
+ # " --main 'README' --title '#{PROJECT_NAME}' -T 'html' #{RDOC_FILES}"
67
+ # end
68
+ # else
69
+ # creates an "rdoc" task
70
+ Rake::RDocTask.new do | rd |
71
+ rd.main = 'README'
72
+ rd.title = PROJECT_NAME
73
+ rd.rdoc_files.include('README', 'TODO', 'lib/**/*.rb')
74
+ end
75
+ # end
76
+
77
+ task :rubyforge => [:rdoc] do
78
+ Rake::RubyForgePublisher.new(PROJECT_NAME, RUBYFORGE_USER).upload
79
+ end
80
+
81
+ task :test do
82
+ Rake::run_tests
83
+ end
data/TODO ADDED
@@ -0,0 +1,26 @@
1
+ == Bugs
2
+
3
+ - Pawn#could_perform_move does not check for en passant.
4
+
5
+ - Move#initialize does not handle "e.p." or "ep" in move text.
6
+
7
+ == To Do
8
+
9
+ - better way to specify configs/program numbers; file must not live in same
10
+ directory as source code
11
+
12
+ - write "-c" code in bin/bangkok and examples
13
+
14
+ - test bankgok -c examples/program_changes.rb examples/game.pgn
15
+
16
+ - implement check for en passant in Pawn#could_perform_move. Might need a new
17
+ ivar @moved_two_spaces_on_first_move.
18
+
19
+ - glide between squares
20
+
21
+ - time to move relative to distance travelled (quarter note per unit;
22
+ length_to_delta(dist))
23
+
24
+ - configuration file: volume offset, min, max; other params
25
+
26
+ - more tests
@@ -0,0 +1,40 @@
1
+ require 'getoptlong'
2
+ begin
3
+ require 'bangkok/chessgame'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ require_gem 'bangkok/chessgame'
7
+ end
8
+
9
+ def usage
10
+ $stderr.puts <<EOS
11
+ usage: bangkok [-v] chessgame.pgn ...
12
+ -v Verbose (prints each move)
13
+ -h This message
14
+
15
+ Using the default GameListener, creates chessgame.mid for every chessgame.pgn.
16
+ To use your own GameListener, see the Bangkok README file.
17
+ EOS
18
+
19
+ exit 1
20
+ end
21
+
22
+ $verbose = false
23
+ g = GetoptLong.new(['-v', '--verbose', GetoptLong::NO_ARGUMENT],
24
+ ['-h', '--help', GetoptLong::NO_ARGUMENT])
25
+ g.each { | opt, arg |
26
+ case opt
27
+ when '-v'
28
+ $verbose = true
29
+ else
30
+ usage
31
+ end
32
+ }
33
+ usage if ARGV.length == 0
34
+
35
+ ARGV.each { | fname |
36
+ game = ChessGame.new
37
+ File.open(fname, 'r') { | f | game.read_moves(f) }
38
+ midi_file_name = File.basename(fname).sub(/(\..+)?$/
39
+ File.open(midi_file_name, '.mid'), 'wb') { | f | game.play(f) }
40
+ }
@@ -0,0 +1,71 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ begin
5
+ require 'bangkok/chessgame'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require_gem 'bangkok/chessgame'
9
+ end
10
+
11
+ class Announcer
12
+ def start_game(io)
13
+ @out = io
14
+ @out.puts 'Start of game'
15
+ end
16
+
17
+ def end_game
18
+ @out.puts 'End of game'
19
+ end
20
+
21
+ def move(piece, from, to)
22
+ @out.puts "Piece #{piece} moving to #{to}"
23
+ end
24
+
25
+ def capture(attacker, loser)
26
+ @out.puts "#{attacker} captures #{loser}"
27
+ end
28
+
29
+ def check
30
+ @out.puts "Check"
31
+ end
32
+
33
+ def checkmate
34
+ @out.puts "Checkmate"
35
+ end
36
+
37
+ def pawn_to_queen(pawn)
38
+ @out.puts "#{pawn} has been turned into a queen"
39
+ end
40
+ end
41
+
42
+ def usage
43
+ $stderr.puts <<EOS
44
+ usage: announcer [-v] chessgame.pgn ...
45
+ -v Verbose (prints each move)
46
+ -h This message
47
+
48
+ For each chess game, prints each event to stdout.
49
+ EOS
50
+
51
+ exit 1
52
+ end
53
+
54
+ $verbose = false
55
+ g = GetoptLong.new(['-v', '--verbose', GetoptLong::NO_ARGUMENT],
56
+ ['-h', '--help', GetoptLong::NO_ARGUMENT])
57
+ g.each { | opt, arg |
58
+ case opt
59
+ when '-v'
60
+ $verbose = true
61
+ else
62
+ usage
63
+ end
64
+ }
65
+ usage if ARGV.length == 0
66
+
67
+ ARGV.each { | fname |
68
+ game = ChessGame.new(Announcer.new)
69
+ File.open(fname, 'r') { | f | game.read_moves(f) }
70
+ game.play($stdout)
71
+ }