just_backgammon 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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +49 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +68 -0
  8. data/Rakefile +10 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/just_backgammon.gemspec +27 -0
  12. data/lib/just_backgammon/bar.rb +107 -0
  13. data/lib/just_backgammon/combined_move.rb +55 -0
  14. data/lib/just_backgammon/common.rb +38 -0
  15. data/lib/just_backgammon/dice_set.rb +74 -0
  16. data/lib/just_backgammon/die.rb +54 -0
  17. data/lib/just_backgammon/errors/bear_off_error.rb +20 -0
  18. data/lib/just_backgammon/errors/blocked_error.rb +20 -0
  19. data/lib/just_backgammon/errors/dice_mismatch_error.rb +20 -0
  20. data/lib/just_backgammon/errors/empty_bar_error.rb +20 -0
  21. data/lib/just_backgammon/errors/empty_point_error.rb +20 -0
  22. data/lib/just_backgammon/errors/moves_possible_error.rb +20 -0
  23. data/lib/just_backgammon/errors/not_players_turn_error.rb +20 -0
  24. data/lib/just_backgammon/errors/pieces_on_bar_error.rb +20 -0
  25. data/lib/just_backgammon/errors/point_not_found_error.rb +20 -0
  26. data/lib/just_backgammon/errors/point_ownership_error.rb +20 -0
  27. data/lib/just_backgammon/errors/wrong_direction_error.rb +20 -0
  28. data/lib/just_backgammon/errors/wrong_phase_error.rb +20 -0
  29. data/lib/just_backgammon/game_state.rb +332 -0
  30. data/lib/just_backgammon/move.rb +112 -0
  31. data/lib/just_backgammon/move_list.rb +151 -0
  32. data/lib/just_backgammon/off_board.rb +73 -0
  33. data/lib/just_backgammon/piece.rb +33 -0
  34. data/lib/just_backgammon/point.rb +104 -0
  35. data/lib/just_backgammon/point_set.rb +95 -0
  36. data/lib/just_backgammon/version.rb +4 -0
  37. data/lib/just_backgammon.rb +6 -0
  38. metadata +123 -0
@@ -0,0 +1,151 @@
1
+ require 'just_backgammon/combined_move'
2
+
3
+ module JustBackgammon
4
+
5
+ # = MoveList
6
+ #
7
+ # A list of moves.
8
+ class MoveList
9
+ extend Forwardable
10
+
11
+ # A new instance of MoveList.
12
+ #
13
+ # @param [Array<Move>] moves
14
+ # All the moves.
15
+ #
16
+ # ==== Example:
17
+ # # Instantiates a new MoveList
18
+ # JustBackgammon::MoveList.new([move_a, move_b])
19
+ def initialize(moves:)
20
+ @moves = moves
21
+ end
22
+
23
+ # @return [Array<Move>] all the moves
24
+ attr_reader :moves
25
+
26
+ def_delegator :moves, :each
27
+ def_delegator :moves, :map
28
+
29
+ # Combines moves if there are any that start where another ends.
30
+ #
31
+ # @return [Array<CombinedMove>]
32
+ def combined_moves
33
+ combined_data = moves.inject([]) do |combined, m|
34
+ matching_move = combined.find_index { |c| c.last.number == m.from.number }
35
+ if matching_move
36
+ combined[matching_move].push(m.to)
37
+ else
38
+ combined.push([m.from, m.to])
39
+ end
40
+
41
+ combined
42
+ end
43
+
44
+ combined_data.map { |legs| JustBackgammon::CombinedMove.new(legs: legs) }
45
+ end
46
+
47
+ # For any of the combined moves, checks if there is one with more two legs or more.
48
+ #
49
+ # @return [Boolean]
50
+ def piece_moves_multiple_times?
51
+ combined_moves.any? { |c| c.size > 2 }
52
+ end
53
+
54
+ # Checks if any move have no points.
55
+ #
56
+ # @return [Boolean]
57
+ def any_missing_point?
58
+ moves.any? { |m| m.missing_point? }
59
+ end
60
+
61
+ # Checks if any move have empty points.
62
+ #
63
+ # @return [Boolean]
64
+ def any_point_empty?
65
+ combined_moves.any? { |m| m.from_point? && m.empty? }
66
+ end
67
+
68
+ # Checks if any move have is owned by the opponent.
69
+ #
70
+ # @return [Boolean]
71
+ def any_point_owned_by_opponent?(current_player_number)
72
+ combined_moves.any? { |m| m.from_point? && m.owned_by_opponent?(current_player_number) }
73
+ end
74
+
75
+ # Checks if any move is from an empty bar.
76
+ #
77
+ # @return [Boolean]
78
+ def any_bar_empty_for_player?(current_player_number)
79
+ moves.any? { |m| m.from_bar? && m.empty_for_player?(current_player_number) }
80
+ end
81
+
82
+ # Checks if any move is blocked from moving.
83
+ #
84
+ # @return [Boolean]
85
+ def any_blocked?(current_player_number)
86
+ moves.any? { |m| m.blocked?(current_player_number) }
87
+ end
88
+
89
+ # Checks if any move is going the wrong direction.
90
+ #
91
+ # @return [Boolean]
92
+ def any_wrong_direction?(current_player_number)
93
+ moves.any? { |m| m.wrong_direction?(current_player_number) }
94
+ end
95
+
96
+ # Checks if any move is bearing off.
97
+ #
98
+ # @return [Boolean]
99
+ def any_bear_off?
100
+ moves.any? { |m| m.bear_off? }
101
+ end
102
+
103
+ # Checks if all moves are from the bar.
104
+ #
105
+ # @return [Boolean]
106
+ def all_moves_from_bar?
107
+ moves.all? { |m| m.from_bar? }
108
+ end
109
+
110
+ # The number of moves from the bar.
111
+ #
112
+ # @return [Fixnum]
113
+ def number_of_moves_from_bar
114
+ moves.select { |m| m.from_bar? }.size
115
+ end
116
+
117
+ # How far each of the moves go.
118
+ #
119
+ # @return [Array<Fixnum>]
120
+ def absolute_distances(current_player_number)
121
+ moves.map { |m| m.absolute_distance_for_player(current_player_number) }
122
+ end
123
+
124
+ # Checks if any of the moves don't match the dice rolls
125
+ #
126
+ # @return [Boolean]
127
+ def dice_mismatch?(current_player_number, dice)
128
+ unallocated = dice.numbers
129
+ allocated = []
130
+
131
+ moves.each do |m|
132
+ move_distance = m.absolute_distance_for_player(current_player_number)
133
+
134
+ index = unallocated.index do |d|
135
+ if m.bear_off?
136
+ d >= move_distance
137
+ else
138
+ d == move_distance
139
+ end
140
+ end
141
+
142
+ if index
143
+ die = unallocated.delete_at(index)
144
+ allocated.push(die)
145
+ end
146
+ end
147
+
148
+ allocated.size != moves.size
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,73 @@
1
+ require 'just_backgammon/off_board'
2
+ require 'just_backgammon/piece'
3
+
4
+ module JustBackgammon
5
+
6
+ # = OffBoard
7
+ #
8
+ # The off board is where pieces go when bearing off. Contains an array of pieces.
9
+ class OffBoard
10
+ extend Common
11
+
12
+ # A new instance of OffBoard.
13
+ #
14
+ # @param [Array<Hash>] pieces
15
+ # All the pieces off board, each piece has an owner.
16
+ #
17
+ # ==== Example:
18
+ # # Instantiates a new OffBoard
19
+ # JustBackgammon::OffBoard.new({
20
+ # pieces: [{owner: 1}, {owner: 1}]
21
+ # })
22
+ def initialize(pieces:)
23
+ @pieces = Piece.load(pieces)
24
+ end
25
+
26
+ # @return [Array<Piece>] all the pieces on the bar, each piece has an owner
27
+ attr_reader :pieces
28
+
29
+ # The identifier of the bar, returns the string 'bar'.
30
+ #
31
+ # @return [String]
32
+ def number
33
+ 'off_board'
34
+ end
35
+
36
+ # ALl the pieces owned by the specified player.
37
+ #
38
+ # @param [Fixnum] player_number
39
+ # the specified player number.
40
+ #
41
+ # @return [Array<Piece>]
42
+ def pieces_owned_by_player(player_number)
43
+ pieces.select { |p| p.owner == player_number }
44
+ end
45
+
46
+ # Number of pieces owned by the specified player.
47
+ #
48
+ # @param [Fixnum] player_number
49
+ # the specified player number.
50
+ #
51
+ # @return [Fixnum]
52
+ def number_of_pieces_owned_by_player(player_number)
53
+ pieces_owned_by_player(player_number).size
54
+ end
55
+
56
+ # Adds a piece off board.
57
+ #
58
+ # @param [Piece] piece
59
+ # the piece to push off board.
60
+ #
61
+ # @return [Array<Piece>]
62
+ def push(piece)
63
+ @pieces.push(piece)
64
+ end
65
+
66
+ # A hashed serialized representation of off board.
67
+ #
68
+ # @return [Hash]
69
+ def as_json
70
+ { pieces: pieces.map(&:as_json) }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,33 @@
1
+ require 'just_backgammon/common'
2
+
3
+ module JustBackgammon
4
+
5
+ # = Piece
6
+ #
7
+ # A piece owned by a player that moves around the board.
8
+ class Piece
9
+ extend Common
10
+
11
+ # A new instance of Piece.
12
+ #
13
+ # @param [Fixnum] owner
14
+ # The owner of the piece.
15
+ #
16
+ # ==== Example:
17
+ # # Instantiates a new Piece
18
+ # JustBackgammon::Piece.new(1)
19
+ def initialize(owner:)
20
+ @owner = owner
21
+ end
22
+
23
+ # @return [Fixnum] the owner of the piece
24
+ attr_reader :owner
25
+
26
+ # A hashed serialized representation of the piece.
27
+ #
28
+ # @return [Hash]
29
+ def as_json
30
+ { owner: owner }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,104 @@
1
+ require 'just_backgammon/common'
2
+ require 'just_backgammon/piece'
3
+
4
+ module JustBackgammon
5
+
6
+ # = Point
7
+ #
8
+ # A point where pieces can land on. Each one has a number.
9
+ class Point
10
+ extend Common
11
+ extend Forwardable
12
+
13
+ # A new instance of Point.
14
+ #
15
+ # @param [Array<Hash>] pieces
16
+ # All the pieces on this point.
17
+ #
18
+ # @param [Fixnum] number
19
+ # The point number and identifier.
20
+ #
21
+ # ==== Example:
22
+ # # Instantiates a new Point
23
+ # JustBackgammon::Point.new({
24
+ # pieces: [{owner: 1}, {owner: 1}],
25
+ # number: 1
26
+ # })
27
+ def initialize(pieces: , number:)
28
+ @pieces = JustBackgammon::Piece.load(pieces)
29
+ @number = number
30
+ end
31
+
32
+ # @return [Array<Piece>] all the pieces on this point
33
+ attr_reader :pieces
34
+
35
+ # @return [Fixnum] the point number and identifier
36
+ attr_reader :number
37
+
38
+ def_delegator :pieces, :size
39
+ def_delegator :pieces, :empty?
40
+
41
+ # Removes a piece and returns it.
42
+ #
43
+ # @return [Piece,NilClass]
44
+ def pop
45
+ @pieces.pop
46
+ end
47
+
48
+ # Adds a piece to the point.
49
+ #
50
+ # @return [Array<Piece>]
51
+ def push(piece)
52
+ @pieces.push(piece)
53
+ end
54
+
55
+ # Checks if point has pieces owned by the specified player.
56
+ #
57
+ # @return [Boolean]
58
+ def owned_by_player?(player_number)
59
+ pieces.any? { |p| p.owner == player_number }
60
+ end
61
+
62
+ # Checks if point has pieces owned by the opponent of the specified player.
63
+ #
64
+ # @return [Boolean]
65
+ def owned_by_opponent?(player_number)
66
+ pieces.any? { |p| p.owner != player_number }
67
+ end
68
+
69
+ # Checks if point has more than one piece.
70
+ #
71
+ # @return [Boolean]
72
+ def blocked?
73
+ size > 1
74
+ end
75
+
76
+ # Checks if point has only one piece.
77
+ #
78
+ # @return [Boolean]
79
+ def blot?
80
+ size == 1
81
+ end
82
+
83
+ # Checks if point is a home point for specified player.
84
+ #
85
+ # @return [Boolean]
86
+ def home?(player_number)
87
+ case player_number
88
+ when 1
89
+ (19..24).include?(number)
90
+ when 2
91
+ (1..6).include?(number)
92
+ else
93
+ true
94
+ end
95
+ end
96
+
97
+ # A hashed serialized representation of the bar.
98
+ #
99
+ # @return [Hash]
100
+ def as_json
101
+ { number: number, pieces: pieces.map(&:as_json) }
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,95 @@
1
+ require 'just_backgammon/common'
2
+ require 'just_backgammon/point'
3
+
4
+ module JustBackgammon
5
+
6
+ # = PointSet
7
+ #
8
+ # A collection of points.
9
+ class PointSet
10
+ extend Common
11
+ extend Forwardable
12
+
13
+ # A new instance of Bar.
14
+ #
15
+ # @param [Array<Hash>] points
16
+ # All the points in the set.
17
+ #
18
+ # ==== Example:
19
+ # # Instantiates a new PointSet
20
+ # JustBackgammon::PointSet.new({
21
+ # points: [point_a, point_b]
22
+ # })
23
+ def initialize(points:)
24
+ @points = Point.load(points)
25
+ end
26
+
27
+ # @return [Array<Point>] all the points in the set
28
+ attr_reader :points
29
+
30
+ def_delegator :points, :map
31
+ def_delegator :points, :any?
32
+ def_delegator :points, :size
33
+
34
+ # Finds a point with the matching number.
35
+ #
36
+ # @return [Point]
37
+ def find_by_number(number)
38
+ points.find { |p| p.number == number }
39
+ end
40
+
41
+ # Finds all points with pieces that are not yet home for the specified player.
42
+ #
43
+ # @return [PointSet]
44
+ def not_home(player_number)
45
+ not_home_points = points.select { |p| p.owned_by_player?(player_number) && !p.home?(player_number) }
46
+ self.class.new(points: not_home_points)
47
+ end
48
+
49
+ # Checks if any pieces are not yet home for the specified player.
50
+ #
51
+ # @return [Boolean]
52
+ def some_pieces_not_home?(player_number)
53
+ not_home(player_number).any?
54
+ end
55
+
56
+ # Finds all points owned by the specified player.
57
+ #
58
+ # @return [PointSet]
59
+ def owned_by_player(player_number)
60
+ owned = @points.select { |p| p.owned_by_player?(player_number) }
61
+ self.class.new(points: owned)
62
+ end
63
+
64
+ # Finds all destinations points from a point with a dice roll for the specified player.
65
+ #
66
+ # @return [PointSet]
67
+ def destinations(from, dice, player_number)
68
+ in_range = dice.map { |d| destination(from, d, player_number) }.compact
69
+ possible = in_range.select { |p| p.empty? || p.owned_by_player?(player_number) || p.blot? }
70
+ self.class.new(points: possible)
71
+ end
72
+
73
+ # A hashed serialized representation of the bar.
74
+ #
75
+ # @return [Hash]
76
+ def as_json
77
+ points.map(&:as_json)
78
+ end
79
+
80
+ private
81
+
82
+ def destination(from, die, player_number) # :nodoc:
83
+ case player_number
84
+ when 1
85
+ from_number = from.instance_of?(Bar) ? 0 : from.number
86
+ find_by_number(from_number + die.number)
87
+ when 2
88
+ from_number = from.instance_of?(Bar) ? 25 : from.number
89
+ find_by_number(from_number - die.number)
90
+ else
91
+ nil
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,4 @@
1
+ module JustBackgammon
2
+ # :nodoc:
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,6 @@
1
+ # :nodoc:
2
+ module JustBackgammon
3
+
4
+ end
5
+
6
+ require 'just_backgammon/game_state'
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: just_backgammon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark Humphreys
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: Provides a representation of a backgammon game complete with rules enforcement
56
+ and serialisation.
57
+ email:
58
+ - mark@mrlhumphreys.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - just_backgammon.gemspec
73
+ - lib/just_backgammon.rb
74
+ - lib/just_backgammon/bar.rb
75
+ - lib/just_backgammon/combined_move.rb
76
+ - lib/just_backgammon/common.rb
77
+ - lib/just_backgammon/dice_set.rb
78
+ - lib/just_backgammon/die.rb
79
+ - lib/just_backgammon/errors/bear_off_error.rb
80
+ - lib/just_backgammon/errors/blocked_error.rb
81
+ - lib/just_backgammon/errors/dice_mismatch_error.rb
82
+ - lib/just_backgammon/errors/empty_bar_error.rb
83
+ - lib/just_backgammon/errors/empty_point_error.rb
84
+ - lib/just_backgammon/errors/moves_possible_error.rb
85
+ - lib/just_backgammon/errors/not_players_turn_error.rb
86
+ - lib/just_backgammon/errors/pieces_on_bar_error.rb
87
+ - lib/just_backgammon/errors/point_not_found_error.rb
88
+ - lib/just_backgammon/errors/point_ownership_error.rb
89
+ - lib/just_backgammon/errors/wrong_direction_error.rb
90
+ - lib/just_backgammon/errors/wrong_phase_error.rb
91
+ - lib/just_backgammon/game_state.rb
92
+ - lib/just_backgammon/move.rb
93
+ - lib/just_backgammon/move_list.rb
94
+ - lib/just_backgammon/off_board.rb
95
+ - lib/just_backgammon/piece.rb
96
+ - lib/just_backgammon/point.rb
97
+ - lib/just_backgammon/point_set.rb
98
+ - lib/just_backgammon/version.rb
99
+ homepage: https://github.com/mrlhumphreys/just_backgammon
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '2.1'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.5.1
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: A backgammon engine written in ruby
123
+ test_files: []