position-definition.rb 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. module ChessData
  2. # A PositionDefinition is a collection of criteria (matchers) which define
  3. # a valid type of position.
  4. #
  5. # e.g. some kind of Rook endings.
  6. #
  7. # database.select do
  8. # at_least 5, "P"
  9. # exactly 1, "R"
  10. # at_most 4, "p"
  11. # exactly 1, "r"
  12. # end
  13. #
  14. class PositionDefinition
  15. # The constructor evaluates the provided _block_, to set up the user's
  16. # board criteria. It then sets up some default criteria for the king's and
  17. # pieces not covered by the block.
  18. def initialize(&block)
  19. @matchers = []
  20. instance_eval(&block)
  21. # fill in a default matcher for any not present
  22. ["K", "k"].each do |piece|
  23. unless @matchers.any? {|matcher| matcher.piece == piece}
  24. exactly 1, piece
  25. end
  26. end
  27. ["P", "p", "N", "n", "B", "b", "R", "r", "Q", "q"].each do |piece|
  28. unless @matchers.any? {|matcher| matcher.piece == piece}
  29. exactly 0, piece
  30. end
  31. end
  32. end
  33. # Checks that all the criteria are matched by the given board.
  34. def check board
  35. @matchers.all? do |matcher|
  36. matcher.check board
  37. end
  38. end
  39. private
  40. # following methods used in block to constructor as DSL
  41. # for a pattern definition
  42. def exactly n, *pieces
  43. pieces.each do |piece|
  44. @matchers << ExactCount.new(n, piece)
  45. end
  46. end
  47. def at_least n, *pieces
  48. pieces.each do |piece|
  49. @matchers << AtLeastCount.new(n, piece)
  50. end
  51. end
  52. def at_most n, *pieces
  53. pieces.each do |piece|
  54. @matchers << AtMostCount.new(n, piece)
  55. end
  56. end
  57. end
  58. # parent class for the different types of count comparators
  59. class Counter
  60. attr_reader :piece
  61. # Each counter has a target _piece_ and reference number _n_.
  62. def initialize n, piece
  63. @n = n
  64. @piece = piece
  65. end
  66. end
  67. # The ExactCount class checks that there are exactly _n_ of the
  68. # named piece on the board.
  69. class ExactCount < Counter
  70. # Returns true if board contains _n_ of _piece_
  71. def check board
  72. board.count(@piece) == @n
  73. end
  74. end
  75. # The AtLeastCount class checks that there are at least _n_ of the
  76. # named piece on the board.
  77. class AtLeastCount < Counter
  78. # Returns true if board contains at least _n_ of _piece_
  79. def check board
  80. board.count(@piece) >= @n
  81. end
  82. end
  83. # The AtMostCount class checks that there are at most _n_ of the
  84. # named piece on the board.
  85. class AtMostCount < Counter
  86. # Returns true if board contains at most _n_ of _piece_
  87. def check board
  88. board.count(@piece) <= @n
  89. end
  90. end
  91. end