bangkok 0.1.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/ChangeLog +5 -0
- data/Credits +6 -0
- data/README +227 -0
- data/Rakefile +83 -0
- data/TODO +26 -0
- data/bin/bangkok +40 -0
- data/examples/announcer.rb +71 -0
- data/examples/game.pgn +15 -0
- data/examples/program_changes.rb +17 -0
- data/install.rb +95 -0
- data/lib/bangkok.rb +2 -0
- data/lib/bangkok/board.rb +110 -0
- data/lib/bangkok/chessgame.rb +50 -0
- data/lib/bangkok/gamelistener.rb +181 -0
- data/lib/bangkok/info.rb +6 -0
- data/lib/bangkok/move.rb +106 -0
- data/lib/bangkok/piece.rb +243 -0
- data/lib/bangkok/square.rb +47 -0
- data/test/mock_game_listener.rb +22 -0
- data/test/test_piece.rb +106 -0
- data/test/test_square.rb +109 -0
- metadata +75 -0
data/ChangeLog
ADDED
data/Credits
ADDED
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.
|
data/Rakefile
ADDED
@@ -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
|
data/bin/bangkok
ADDED
@@ -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
|
+
}
|