From 0e3ef5c33ed0fa79d3fd3b61288d76079d780fc5 Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Fri, 26 Jun 2009 09:55:58 +0200 Subject: [PATCH] Introduce the concept of plugin interface. A plugin interface is a loose set of requirements a plugin must comply to. Plugins declare the interface they implement in their initialization line. --- lib/board/table.rb | 8 ++--- lib/factory.rb | 5 ++- lib/games/chess/main.rb | 1 + lib/mainwindow.rb | 2 +- lib/plugins/celtic/celtic.rb | 3 +- lib/plugins/clocks/digital.rb | 2 +- lib/plugins/fantasy/fantasy.rb | 3 +- lib/plugins/games/chess.rb | 59 +++++++++++++++++++++++++++++++++ lib/plugins/games/shogi.rb | 36 ++++++++++++++++++++ lib/plugins/layouts/cool/cool.rb | 2 +- lib/plugins/loader.rb | 12 ++++--- lib/plugins/movelist/simple_movelist.rb | 2 +- lib/plugins/plugin.rb | 10 +++--- lib/plugins/shogi/shogi.rb | 6 ++-- lib/plugins/squares/default.rb | 3 +- 15 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 lib/plugins/games/chess.rb create mode 100644 lib/plugins/games/shogi.rb diff --git a/lib/board/table.rb b/lib/board/table.rb index 4b505af..e843ffc 100644 --- a/lib/board/table.rb +++ b/lib/board/table.rb @@ -26,13 +26,13 @@ class Table < Qt::GraphicsView # load theme @theme = Theme.new @theme.pieces = @loader. - get_matching((game.keywords || []) + %w(pieces)). + get_matching(:pieces, game.keywords || []). new(:game => game, :shadow => true) @theme.board = @loader. - get_matching(%w(board), game.keywords || []). + get_matching(:board, game.keywords || []). new(:game => game) @theme.layout = @loader. - get_matching(%w(layout), game.keywords || []). + get_matching(:layout, game.keywords || []). new(game) # recreate elements @@ -46,7 +46,7 @@ class Table < Qt::GraphicsView else {} end - clock_class = @loader.get_matching(%w(clock)) + clock_class = @loader.get_matching(:clock) @elements[:clocks] = game.players.inject({}) do |res, player| res[player] = clock_class.new(scene) res diff --git a/lib/factory.rb b/lib/factory.rb index 0ffbabc..1899b36 100644 --- a/lib/factory.rb +++ b/lib/factory.rb @@ -1,6 +1,9 @@ class Factory - def initialize(&blk) + attr_reader :component + + def initialize(klass = nil, &blk) @blk = blk + @component = klass end def new(*args) diff --git a/lib/games/chess/main.rb b/lib/games/chess/main.rb index e4cb490..77f0e11 100644 --- a/lib/games/chess/main.rb +++ b/lib/games/chess/main.rb @@ -22,6 +22,7 @@ Game.add :chess do :validator => Validator, :piece => Piece, :players => [:white, :black], + :types => [:pawn, :knight, :bishop, :rook, :queen, :king], :serializer => lambda {|rep| Serializer.new(rep, validator, move, piece) }, :keywords => %w(chess), diff --git a/lib/mainwindow.rb b/lib/mainwindow.rb index 25803dd..3ef1963 100644 --- a/lib/mainwindow.rb +++ b/lib/mainwindow.rb @@ -74,7 +74,7 @@ private @table = Table.new scene, @loader, self @controller = Controller.new(@table) - movelist = @loader.get_matching(%w(movelist)).new(@controller) + movelist = @loader.get_matching(:movelist).new(@controller) movelist_dock = Qt::DockWidget.new(self) movelist_dock.widget = movelist movelist_dock.window_title = KDE.i18n("History") diff --git a/lib/plugins/celtic/celtic.rb b/lib/plugins/celtic/celtic.rb index 87eac1a..d663de1 100644 --- a/lib/plugins/celtic/celtic.rb +++ b/lib/plugins/celtic/celtic.rb @@ -4,7 +4,8 @@ require 'plugins/svg_theme' class CelticTheme < SvgTheme include Plugin plugin :name => 'Celtic', - :keywords => %w(chess pieces) + :interface => :pieces, + :keywords => %w(chess) def initialize(opts = {}) super(opts) diff --git a/lib/plugins/clocks/digital.rb b/lib/plugins/clocks/digital.rb index 80b51d1..de06d96 100644 --- a/lib/plugins/clocks/digital.rb +++ b/lib/plugins/clocks/digital.rb @@ -9,7 +9,7 @@ class DigitalClock < Qt::GraphicsItemGroup include Observer plugin :name => 'Digital Clock', - :keywords => %w(clock) + :interface => :clock attr_reader :items, :rect, :clock diff --git a/lib/plugins/fantasy/fantasy.rb b/lib/plugins/fantasy/fantasy.rb index 72fbf2e..06fb2be 100644 --- a/lib/plugins/fantasy/fantasy.rb +++ b/lib/plugins/fantasy/fantasy.rb @@ -4,7 +4,8 @@ require 'plugins/svg_theme' class FantasyTheme < SvgTheme include Plugin plugin :name => 'Fantasy', - :keywords => %w(chess pieces) + :interface => :pieces, + :keywords => %w(chess) def initialize(opts = {}) super(opts) diff --git a/lib/plugins/games/chess.rb b/lib/plugins/games/chess.rb new file mode 100644 index 0000000..821aacf --- /dev/null +++ b/lib/plugins/games/chess.rb @@ -0,0 +1,59 @@ +require 'games/games' +require 'games/chess/state' +require 'games/chess/move' +require 'games/chess/board' +require 'games/chess/policy' +require 'games/chess/animator' +require 'games/chess/validator' +require 'games/chess/serializer' +require 'games/chess/pgn' +require 'plugins/plugin' + +class ChessGame + include Plugin + + plugin :name => 'Chess', + :id => :chess, + :interface => :game, + :keywords => %w(chess) + + attr_reader :size, :policy, :state, :board, :move, + :animator, :validator, :piece, :players, + :types, :serializer, :game_writer, + :game_extensions + + def initialize + @size = Point.new(8, 8) + @policy = Policy.new(Move) + @state_component = State + @state = Factory.new(State) { State.new(board.new, move, piece) } + @board = Factory.new(Board) { Board.new(size) } + @move = Move + @animator = Animator + @validator = Validator + @piece = Piece + @players = [:white, :black] + @types = [:pawn, :knight,:bishop, :rook, :queen, :king] + @serializer = lambda {|rep| + Serializer.new(rep, validator, move, piece) } + @keywords = %w(chess) + + @game_writer = PGN.new(serializer.new(:compact), state) + @game_extensions = %w(pgn) + end + + def game_reader + @game_writer + end +end + +class Chess5x5Game < ChessGame + plugin :name => 'Chess 5x5', + :id => :chess5x5, + :interface => :game, + :keywords => %w(chess) + + def initialize + @size = Point.new(5, 5) + end +end diff --git a/lib/plugins/games/shogi.rb b/lib/plugins/games/shogi.rb new file mode 100644 index 0000000..97a19e3 --- /dev/null +++ b/lib/plugins/games/shogi.rb @@ -0,0 +1,36 @@ +require 'games/shogi/state' +require 'games/shogi/pool' +require 'games/shogi/move' +require 'games/shogi/validator' +require 'games/shogi/policy' +require 'plugins/plugin' + +class ShogiGame + include Plugin + + plugin :name => 'Shogi', + :id => :shogi, + :interface => :game, + :keywords => %w(shogi), + :depends => [:chess] + + attr_reader :size, :state, :board, :pool, + :policy, :move, :animator, :validator, + :piece, :keywords, :players, :types, :actions + + def initialize + @size = Point.new(9, 9) + @state = Factory.new { State.new(board.new, pool, move, piece) } + @board = Factory.new { deps[:chess].board.component.new size } + @pool = Pool + @piece = deps[:chess].piece + @move = Move + @validator = Validator + @animator = deps[:chess].animator + @policy = Policy.new(move, validator) + + @players = [:white, :black] + @types = [:pawn, :lance, :horse, :silver, + :gold, :bishop, :rook, :king] + end +end diff --git a/lib/plugins/layouts/cool/cool.rb b/lib/plugins/layouts/cool/cool.rb index 3c86c6c..127dbd1 100644 --- a/lib/plugins/layouts/cool/cool.rb +++ b/lib/plugins/layouts/cool/cool.rb @@ -4,7 +4,7 @@ class CoolLayout include Plugin plugin :name => 'Layouts/Cool', - :keywords => %w(layout) + :interface => :layout # values relative to unit = 1 MARGIN = 0.2 diff --git a/lib/plugins/loader.rb b/lib/plugins/loader.rb index c78b8e5..f1babfb 100644 --- a/lib/plugins/loader.rb +++ b/lib/plugins/loader.rb @@ -27,11 +27,15 @@ class PluginLoader @plugins.each_value(&blk) end - def get_matching(required, optional = []) - plugins = @plugins.values. - reject {|x| not x.matches?(required) }. - sort_by {|x| x.score(optional) } + def get_matching(interface, keywords = []) + plugins = get_all_matching(interface). + sort_by {|x| x.score(keywords) } + raise NoPluginFound if plugins.empty? plugins.last end + + def get_all_matching(interface) + @plugins.values.reject {|x| not x.implements?(interface) } + end end diff --git a/lib/plugins/movelist/simple_movelist.rb b/lib/plugins/movelist/simple_movelist.rb index 786598a..b32a95f 100644 --- a/lib/plugins/movelist/simple_movelist.rb +++ b/lib/plugins/movelist/simple_movelist.rb @@ -5,7 +5,7 @@ class SimpleMoveList < Qt::ListView include Observer plugin :name => 'Simple Move List', - :keywords => %w(movelist) + :interface => :movelist class LinearHistoryModel < Qt::StringListModel include Observer diff --git a/lib/plugins/plugin.rb b/lib/plugins/plugin.rb index 928c81b..7ff7d5a 100644 --- a/lib/plugins/plugin.rb +++ b/lib/plugins/plugin.rb @@ -18,14 +18,12 @@ module Plugin @plugin_data[:name] if @plugin_data end - def matches?(keywords) - keywords.all? do |k| - @plugin_data[:keywords].include? k - end + def score(keywords) + ((@plugin_data[:keywords] || []) & keywords).size end - def score(keywords) - (@plugin_data[:keywords] & keywords).size + def implements?(iface) + @plugin_data[:interface] == iface end end diff --git a/lib/plugins/shogi/shogi.rb b/lib/plugins/shogi/shogi.rb index 7a44127..6a14430 100644 --- a/lib/plugins/shogi/shogi.rb +++ b/lib/plugins/shogi/shogi.rb @@ -12,7 +12,8 @@ class ShogibanBackground BASE_DIR = File.dirname(__FILE__) plugin :name => 'Shogiban', - :keywords => %w(shogi board) + :interface => :board, + :keywords => %w(shogi) def initialize(opts = {}) @squares = opts[:board_size] || opts[:game].size @@ -64,7 +65,8 @@ class ShogiTheme :pawn => 0.8 } plugin :name => 'Shogi', - :keywords => %w(shogi pieces) + :interface => :pieces, + :keywords => %w(shogi) def initialize(opts = {}) @loader = lambda do |piece, size| diff --git a/lib/plugins/squares/default.rb b/lib/plugins/squares/default.rb index 6da25d9..443c8f6 100644 --- a/lib/plugins/squares/default.rb +++ b/lib/plugins/squares/default.rb @@ -6,7 +6,8 @@ class DefaultBackground include Background plugin :name => 'Default', - :keywords => %w(chess board) + :interface => :board, + :keywords => %w(chess) def initialize(opts) @squares = opts[:board_size] || opts[:game].size -- 2.11.4.GIT