boardgame_engine 0.2.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8a00224a55f64e290296e59c90686e9984141c43b3ce1d588cbe2a6f382d906
4
- data.tar.gz: 83ec3bd763ae34688b1f99f8e2c54d9bc9c0229430fc80ae5211db81a9ce47a0
3
+ metadata.gz: 8f3c14dd095359859bb7c6fb53b25e7c94ae18bb1b4e407d0e377797254930a6
4
+ data.tar.gz: 5ecf4d791871a327a1f2e2844cc21b5d0de4b23b001f478a8ef8ca89834bc803
5
5
  SHA512:
6
- metadata.gz: c3d1d0647a5ff2b85b619aa85f72dc6a1f3638074a2333b22fed2535239c0feb64c43e4ac97eb98bbff45c77ebf513bd9d401299b8046483a2520dca005fdf1f
7
- data.tar.gz: eb8768b54c655792cf5f5270ddcc30867b1afd4e80d8382e97704873742b1dd8964925ed1683eff327b386c0d0f9d2aebf454ff1ecfe4cd1bed326dfd1cb3c41
6
+ metadata.gz: 8a8579be5461dda2d3e77a4c2fa50b92ff0a6d4179f960e2ce083261bbc5db6cfe05defa5f1e679e07576bd517d2296477666b86f4388be30030b8127a2ce5ef
7
+ data.tar.gz: afd22e1654d2ca4a66f648e96d08cfd9f52ba3c780d19d4352344214dd6b753ba85236f8e2b0bbf47ecce2c1bd272ac6c33ed6592c0a96838db3c0ea50b327c5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boardgame_engine (0.1.1)
4
+ boardgame_engine (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A gem that provides a template for creating boardgames. It aims to streamline
4
4
  the process of making boardgames to be played on the terminal by providing a
5
- variety of methods and classes.
5
+ variety of classes and modules.
6
6
 
7
7
  ## Installation
8
8
 
@@ -15,8 +15,43 @@ If bundler is not being used to manage dependencies, install the gem by executin
15
15
  $ gem install boardgame_engine
16
16
 
17
17
  ## Usage
18
+ ### Initial Setup
19
+ To start making your own boardgame using this gem, your game module needs at least two classes -
20
+ a `Game` class inheriting from `Boardgame` and a `Board` class inheriting `Board`.
21
+ `Game` captures the core gameplay loop/logic and the `Board` captures
22
+ interactions with the board.
18
23
 
19
- TODO: Write usage instructions here
24
+ Depending on the game rules, select child modules from the `Games` and `Boards`
25
+ and include it into the `Game` and `Board` class respectively.
26
+ These modules will handle much of the game and board logic related to the
27
+ game mechanic/rule.
28
+
29
+ For example, if the turns in your game go in a cycle like 1 -> 2 -> 3 -> 1 -> 2 ...
30
+ your `Game` class would look like
31
+ ```ruby
32
+ class Game < BoardgameEngine::Game
33
+ include Games::CyclicalGame
34
+ NUM_PLAYERS = 3
35
+ ...
36
+ end
37
+ ```
38
+ Including this module makes it so that the `BoardgameEngine::Game#change_turn`
39
+ method automatically hands the turn over to the correct player.
40
+
41
+ ### Overriding
42
+ At the current stage of the app, there are methods that necessarily must be
43
+ overridden in the child class.
44
+ `play_turn` must be implemented on _your_ `Game` class, and must contain what happens
45
+ during a single turn (not a round, just a turn).
46
+
47
+ Additionally, the limited number of modules mean that you will probably have to implement some
48
+ methods on your own. I hope to reduce the number of these as the app matures.
49
+ I left yard comments on all module methods, so hopefully overriding won't be too
50
+ difficult.
51
+
52
+ ## Example Games
53
+ Check out `connect4.rb` for a game built with rather minimal overrides and
54
+ `chess.rb` for a game that makes heavy use of customization.
20
55
 
21
56
  ## Development
22
57
 
@@ -26,4 +61,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
26
61
 
27
62
  ## Contributing
28
63
 
29
- Bug reports and pull requests are welcome on GitHub at https://github.com/Forthoney/boardgame_engine.
64
+ Bug reports are welcome on GitHub at https://github.com/Forthoney/boardgame_engine.
65
+ I may not get to them quickly but it will be greatly helpful
66
+
67
+ As the app is under heavy development I will not look deeply into actually
68
+ pulling pull requests (if there were to be any).
69
+ I will definitely consider the ideas proposed in them, so they are still welcome.
@@ -4,17 +4,7 @@
4
4
  module Boards
5
5
  # Boards laid out in grid format
6
6
  module Grid
7
- # Make a 2D Array representing the board
8
- #
9
- # @param [Integer] row the number of rows
10
- # @param [Integer] col the number of columns
11
- #
12
- # @return [Array<Array>] A 2D Array
13
- def generate_board(row, col)
14
- Array.new(row) { Array.new(col) { nil } }
15
- end
16
-
17
- # Print a visual representation of the
7
+ # Print a visual representation of the board
18
8
  #
19
9
  # @param [Boolean] show_row whether to show row labels
20
10
  # @param [Boolean] show_col whether to show column labels
@@ -30,19 +20,42 @@ module Boards
30
20
  return unless show_col
31
21
 
32
22
  column_spacer = show_row ? ' ' : ''
33
- puts format_col(column_spacer)
23
+ puts format_col_numbering(column_spacer)
34
24
  end
35
25
 
26
+ # Accessor for getting piece at a location on the board
27
+ #
28
+ # @param [Array<Integer, Integer>] location the coordinates on the grid to
29
+ # get the piece from
30
+ #
31
+ # @return [Piece] the piece at the location
36
32
  def get_piece_at(location)
37
33
  row, col = location
38
34
  @board.dig(row, col)
39
35
  end
40
36
 
37
+ # Setter for setting a piece at a location on the board
38
+ #
39
+ # @param [Array<Integer, Integer>] location <description>
40
+ # @param [Piece] piece the piece to place down
41
+ #
42
+ # @return [void]
41
43
  def set_piece_at(location, piece)
42
44
  row, col = location
43
45
  @board[row][col] = piece
44
46
  end
45
47
 
48
+ # Check whether the given input refers to a valid location on the board,
49
+ # regardless of placement of other pieces, rules, etc
50
+ #
51
+ # @param [String] input the user input
52
+ # @param [Boolean] only_row optional arg for only allowing user to specify
53
+ # row
54
+ # @param [Boolean] only_col optional arg for only allowing user to specify
55
+ # col
56
+ #
57
+ # @return [Boolean] true if the input can be parsed into a location on the
58
+ # board, false otherwise
46
59
  def valid_board_input?(input, only_row: false, only_col: false)
47
60
  if only_row || only_col
48
61
  input.match?(/[0-#{@board.length - 1}]/)
@@ -51,29 +64,154 @@ module Boards
51
64
  end
52
65
  end
53
66
 
67
+ # Check whether there exists a clear diagonal path from start to a
68
+ # destination
69
+ #
70
+ # @param [Array<Integer, Integer>] start_location the start location
71
+ # @param [Array<Integer, Integer>] end_location the intended destination
72
+ #
73
+ # @return [Boolean] true if there exists an unblocked path to destination
74
+ def clear_diag_path?(start_location, end_location)
75
+ row, col = start_location
76
+ end_row, end_col = end_location
77
+
78
+ ((end_row - row).abs == (end_col - col).abs) \
79
+ && clear_path?(row, col, end_row, end_col, board)
80
+ end
81
+
82
+ # Check whether there exists an unblocked horizontal path from start to
83
+ # a destination
84
+ #
85
+ # @param [Array<Integer, Integer>] start_location the start location
86
+ # @param [Array<Integer, Integer>] end_location the intended destination
87
+ #
88
+ # @return [Boolean] true if there exists an unblocked path to destination
89
+ def clear_horz_path?(start_location, end_location)
90
+ row, col = start_location
91
+ end_row, end_col = end_location
92
+
93
+ (end_row == row) && clear_path?(row, col, end_row, end_col, board)
94
+ end
95
+
96
+ # Check whether there exists an unblocked vertical path from start to
97
+ # a destination
98
+ #
99
+ # @param [Array<Integer, Integer>] start_location the start location
100
+ # @param [Array<Integer, Integer>] end_location the intended destination
101
+ #
102
+ # @return [Boolean] true if there exists an unblocked path to destination
103
+ def clear_vert_path?(start_location, end_location)
104
+ row, col = start_location
105
+ end_row, end_col = end_location
106
+
107
+ (end_col == col) && clear_path?(row, col, end_row, end_col, board)
108
+ end
109
+
110
+ # Parse an input from the user that has a corresponding board location. This
111
+ # must be used in after valid_board_input? has confirmed the input is in
112
+ # the correct format
113
+ #
114
+ # @param [String] input a valid
115
+ #
116
+ # @return [Array<Integer, Integer>]
54
117
  def parse_input(input)
55
118
  input.split(',').map(&:to_i)
56
119
  end
57
120
 
58
- def consecutive?(row: true, col: true, diagonal: true)
121
+ # Make a 2D Array representing the board
122
+ #
123
+ # @param [Integer] row the number of rows
124
+ # @param [Integer] col the number of columns
125
+ #
126
+ # @return [Array<Array>] A 2D Array
127
+ def generate_board(row, col)
128
+ Array.new(row) { Array.new(col) { nil } }
129
+ end
130
+
131
+ # Check whether there are consecutive pieces on the board
132
+ #
133
+ # @param [Integer] num the number of consecutive pieces to check for.
134
+ # @param [Boolean] row check for row-wise consecutive pieces
135
+ # @param [Boolean] col check for column-wise consecutive pieces
136
+ # @param [Boolean] diagonal check for diagonally consecutive pieces
137
+ #
138
+ # @return [Boolean] true if any specified directins have num number of
139
+ # consecutive pieces
140
+ def consecutive?(num, row: true, col: true, diagonal: true)
59
141
  configs = []
60
142
  configs << @board if row
61
143
  configs << @board.transpose if col
62
144
  configs << align_diagonal(@board) << align_diagonal(@board.transpose) if diagonal
63
145
 
64
146
  configs.each do |board|
65
- board.each { |array| return true if row_consecutive?(4, array) }
147
+ board.each { |array| return true if row_consecutive?(num, array) }
66
148
  end
67
149
  false
68
150
  end
69
151
 
70
152
  private
71
153
 
154
+ # calculates the location of the next cell in the sequence of cells from a
155
+ # given start location and an end location
156
+ #
157
+ # @param [Integer] row
158
+ # @param [Integer] col
159
+ # @param [Integer] end_row
160
+ # @param [Integer] end_col
161
+ #
162
+ # @return [Array<Integer, Integer>] the next cell in the sequence of cells
163
+ def next_cell(row, col, end_row, end_col)
164
+ row_move = 0
165
+ col_move = 0
166
+
167
+ col_move = (end_col - col) / (end_col - col).abs if end_col != col
168
+ row_move = (end_row - row) / (end_row - row).abs if end_row != row
169
+
170
+ [row + row_move, col + col_move]
171
+ end
172
+
173
+ # Given a linear path, check whether there exists an unblocked path.
174
+ # It msut be checked beforehand that the path is indeed linear
175
+ #
176
+ # @param [Integer] row
177
+ # @param [Integer] col
178
+ # @param [Integer] end_row
179
+ # @param [Integer] end_col
180
+ #
181
+ # @return [Boolean] true if there exists a clear path
182
+ def clear_path?(row, col, end_row, end_col)
183
+ current_tile = get_piece_at([row, col])
184
+
185
+ if (row == end_row) && (col == end_col)
186
+ current_tile.nil? || (current_tile.owner != @owner)
187
+ elsif current_tile.nil? || current_tile.equal?(self)
188
+ next_row, next_col = next_cell(row, col, end_row, end_col)
189
+ clear_path?(next_row, next_col, end_row, end_col)
190
+ else
191
+ false
192
+ end
193
+ end
194
+
195
+ # Check a single row for consecutive pieces
196
+ #
197
+ # @param [Integer] num the number of consecutive pieces to check for
198
+ # @param [Array] array the row to check in
199
+ #
200
+ # @return [Boolean] true if there are at least num number of consecutive
201
+ # elements
72
202
  def row_consecutive?(num, array)
73
- elem_counts = array.chunk { |x| x }.map { |x, xs| [x, xs.length] }
74
- elem_counts.any? { |x, count| count >= num && x }
203
+ chunked_elems = array.chunk { |elem| elem }
204
+ elem_counts = chunked_elems.map { |elem, elems| [elem, elems.length] }
205
+ elem_counts.any? { |elem, count| count >= num && elem }
75
206
  end
76
207
 
208
+ # Align the diagonals of a board. Must be called once on the original board
209
+ # and once more on the transposed board to check for both directions of
210
+ # diagonals
211
+ #
212
+ # @param [Array<Array>] board the board to align
213
+ #
214
+ # @return [Array<Array>] new board with the diagonals aligned
77
215
  def align_diagonal(board)
78
216
  board.map.with_index do |row, idx|
79
217
  left_filler = Array.new(board.length - 1 - idx, nil)
@@ -82,17 +220,36 @@ module Boards
82
220
  end
83
221
  end
84
222
 
223
+ # Format a single row for String representation
224
+ #
225
+ # @param [Array] row the row to turn into a String
226
+ #
227
+ # @return [String] a String representation of the row
85
228
  def format_row(row)
86
229
  row.map { |elem| "[#{elem.nil? ? ' ' : elem}]" }.join
87
230
  end
88
231
 
89
- def format_col(column_spacer)
232
+ # Format the column numbering of a board
233
+ #
234
+ # @param [String] column_spacer the spacer to use between the indices
235
+ #
236
+ # @return [String] a String representation of the row
237
+ def format_col_numbering(column_spacer)
90
238
  @board[0].each_index.reduce(column_spacer) { |str, idx| str + " #{idx} " }
91
239
  end
92
240
  end
93
241
 
94
242
  # Boards with movable pieces
95
243
  module MovablePiece
244
+ # Move a piece from at a start location to an end location. Should be called
245
+ # after checking that the movement is valid board and game logic wise.
246
+ #
247
+ # @param [<Type>] start_location the starting location of the piece
248
+ # @param [<Type>] end_location the destination of the piece
249
+ # @param [<Type>] set_start_to what to place at the start location. defaults
250
+ # to nil
251
+ #
252
+ # @return [Piece] the piece at the destination
96
253
  def move_piece(start_location, end_location, set_start_to: nil)
97
254
  piece = get_piece_at(start_location)
98
255
  set_piece_at(start_location, set_start_to)
@@ -2,52 +2,44 @@
2
2
 
3
3
  # All classes in this script are intended to be abstract, meaning they should
4
4
  # not be called on their own.
5
-
6
5
  module BoardgameEngine
7
- # Class representing a physical board comprised of a grid in a board game. It
8
- # acts as both the View and Model if the project were to be compared to a MVC
9
- # model. It plays both roles as the board in a board game not only stores data,
10
- # but also IS the data that must be shown to the players.
11
- class Board
12
- attr_reader :board
13
-
14
- private
15
-
16
- def spot_playable?(piece, row, col)
17
- piece.possible_moves.include? [row, col]
18
- end
19
- end
20
-
21
6
  # Class representing a player in a game
22
7
  class Player
23
8
  attr_reader :name
24
9
 
25
10
  # Creates a player object with the given name
26
- # @param [String] name
11
+ #
12
+ # @param [String] name the name of the player
27
13
  def initialize(name)
28
14
  @name = name
29
15
  end
30
16
 
17
+ # String representation of player
18
+ #
19
+ # @return [String] the name of the player
31
20
  def to_s
32
21
  @name.to_s
33
22
  end
34
23
  end
35
24
 
36
- # Class representing a board game.
37
- class Boardgame
25
+ # Class representing the game loop. It contains the tutorial sequence and
26
+ # information about the core gameplay loop
27
+ class Game
28
+ PLAY_INSTRUCTIONS = ''
38
29
  EXIT_INSTRUCTIONS ||= "Try a sample input or input 'back' to leave the " \
39
30
  "tutorial. Type in 'exit' anytime to exit the game fully"
40
-
41
- def initialize(board, instructions, names)
42
- @players = names.map { |name| Player.new name }
43
- @board = setup_board(board)
44
- @instructions = instructions
45
- @winner = nil
46
- end
47
-
48
- def self.play(do_onboarding: true, num_players: 2)
31
+ GAME_NAME = 'Boardgame'
32
+ NUM_PLAYERS = 2
33
+
34
+ # Begins a round of the Game
35
+ #
36
+ # @param [Boolean] do_onboarding optional argument on whether to do
37
+ # onboarding
38
+ #
39
+ # @return [void]
40
+ def self.start(do_onboarding: true)
49
41
  names = []
50
- num_players.times do |i|
42
+ self::NUM_PLAYERS.times do |i|
51
43
  puts "What is Player #{i}'s name?"
52
44
  names.push(gets.chomp)
53
45
  end
@@ -57,13 +49,13 @@ module BoardgameEngine
57
49
  puts "Welcome to #{@game}!"
58
50
  @game.onboarding if do_onboarding
59
51
  puts "Starting #{@game}..."
60
- @game.start
61
- end
62
-
63
- def to_s(game_name = 'boardgame')
64
- "#{game_name} between #{@players.join(', ')}"
52
+ @game.play
65
53
  end
66
54
 
55
+ # Execute onboarding sequence where the player is asked if they want a
56
+ # tutorial
57
+ #
58
+ # @return [void]
67
59
  def onboarding
68
60
  puts "Would you like a tutorial on how to play on this program? \n(y, n)"
69
61
 
@@ -78,28 +70,60 @@ module BoardgameEngine
78
70
  end
79
71
  end
80
72
 
81
- def tutorial
82
- puts @instructions + Boardgame::EXIT_INSTRUCTIONS
83
- input = gets.chomp
84
- until input == 'back'
85
- exit if input == 'exit'
86
- puts valid_input?(input) ? 'Valid input!' : 'Invalid input'
87
- input = gets.chomp
88
- end
89
- end
90
-
91
- def start(turn = @players[0])
73
+ # Play the game
74
+ #
75
+ # @param [Player] turn the player who is going first
76
+ #
77
+ # @return [void]
78
+ def play(turn = @players[0])
92
79
  @turn = turn
93
80
  @board.display
94
81
  until @winner
95
82
  play_turn
96
83
  @board.display
84
+ change_turn
97
85
  end
98
86
  puts "#{@winner} wins!"
99
87
  end
100
88
 
89
+ # String representation of the game
90
+ #
91
+ # @return [String] <description>
92
+ def to_s
93
+ "#{self.class::GAME_NAME} between #{@players.join(', ')}"
94
+ end
95
+
101
96
  protected
102
97
 
98
+ # Constructor for a board game. Kept private so that an object of just class
99
+ # BoardGame cannot be instantiated without being inherited. It essentially
100
+ # keeps it as an abstract class
101
+ #
102
+ # @param [Board] board the board to play on
103
+ # @param [Array<String>] names the names of the players
104
+ def initialize(board, names)
105
+ @players = names.map { |name| Player.new name }
106
+ @board = setup_board(board)
107
+ @winner = nil
108
+ end
109
+
110
+ # Run tutorial for the game
111
+ def tutorial
112
+ puts self.class::PLAY_INSTRUCTIONS + self.class::EXIT_INSTRUCTIONS
113
+ input = gets.chomp
114
+ until input == 'back'
115
+ exit if input == 'exit'
116
+ puts @board.valid_board_input?(input) ? 'Valid input' : 'Invalid input'
117
+ input = gets.chomp
118
+ end
119
+ end
120
+
121
+ # Prompts a user for a board input until a proper input is received
122
+ #
123
+ # @param [Array<String>] special_commands a list of commands that are not
124
+ # valid board input but are valid commands like "back"
125
+ #
126
+ # @return [String] a valid input
103
127
  def get_valid_board_input(special_commands = [])
104
128
  input = gets.chomp
105
129
 
@@ -113,6 +137,11 @@ module BoardgameEngine
113
137
  input
114
138
  end
115
139
 
140
+ # Setup the board
141
+ #
142
+ # @param [Class] board the class of the board to be used in the game
143
+ #
144
+ # @return [Board] a new board
116
145
  def setup_board(board)
117
146
  board.new
118
147
  end
@@ -135,44 +164,5 @@ module BoardgameEngine
135
164
  def to_s
136
165
  @name.to_s
137
166
  end
138
-
139
- protected
140
-
141
- def clear_diag_path?(row, col, end_row, end_col, board)
142
- ((end_row - row).abs == (end_col - col).abs) \
143
- && clear_path?(row, col, end_row, end_col, board)
144
- end
145
-
146
- def clear_horz_path?(row, col, end_row, end_col, board)
147
- (end_row == row) && clear_path?(row, col, end_row, end_col, board)
148
- end
149
-
150
- def clear_vert_path?(row, col, end_row, end_col, board)
151
- (end_col == col) && clear_path?(row, col, end_row, end_col, board)
152
- end
153
-
154
- private
155
-
156
- def next_cell(row, col, end_row, end_col)
157
- row_move = 0
158
- col_move = 0
159
-
160
- col_move = (end_col - col) / (end_col - col).abs if end_col != col
161
- row_move = (end_row - row) / (end_row - row).abs if end_row != row
162
-
163
- [row + row_move, col + col_move]
164
- end
165
-
166
- def clear_path?(row, col, end_row, end_col, board)
167
- current_tile = board.dig(row, col)
168
- if (row == end_row) && (col == end_col)
169
- current_tile.nil? || (current_tile.owner != @owner)
170
- elsif current_tile.nil? || current_tile.equal?(self)
171
- next_row, next_col = next_cell(row, col, end_row, end_col)
172
- clear_path?(next_row, next_col, end_row, end_col, board)
173
- else
174
- false
175
- end
176
- end
177
167
  end
178
168
  end
@@ -4,8 +4,47 @@ require 'boardgame_engine/boardgame'
4
4
  require 'boardgame_engine/game_modules'
5
5
  require 'boardgame_engine/board_modules'
6
6
 
7
- module SampleChess
8
- class ChessBoard < BoardgameEngine::Board
7
+ # module for playing a game of chess
8
+ module Chess
9
+ # Class for a game of chess
10
+ class Game < BoardgameEngine::Game
11
+ include Games::CyclicalTurn
12
+ include Games::MovablePiece
13
+
14
+ PLAY_INSTRUCTIONS = 'You can select spots on the board by inputting the ' \
15
+ "row and column with a comma in between. See example below\n1, 1\n"
16
+ GAME_NAME = 'Chess'
17
+ NUM_PLAYERS = 2
18
+
19
+ def initialize(names)
20
+ super(Board, names)
21
+ end
22
+
23
+ private
24
+
25
+ # Check whether a piece can be selected by the player
26
+ #
27
+ # @param [Piece] piece the piece being selected
28
+ #
29
+ # @return [Boolean] whether the piece exists and if so, if it belongs to
30
+ # the player currently going
31
+ def valid_piece?(piece)
32
+ piece && piece.owner == @turn
33
+ end
34
+
35
+ def play_turn
36
+ puts "#{@turn}'s turn"
37
+ killed = play_move
38
+ @winner = @turn if killed.is_a?(King)
39
+ end
40
+
41
+ def setup_board(board)
42
+ board.new(@players[0], @players[1])
43
+ end
44
+ end
45
+
46
+ # Class for a chessboard
47
+ class Board
9
48
  include Boards::Grid
10
49
  include Boards::MovablePiece
11
50
 
@@ -41,38 +80,6 @@ module SampleChess
41
80
  end
42
81
  end
43
82
 
44
- class Chess < BoardgameEngine::Boardgame
45
- include Games::CyclicalTurn
46
- include Games::MovablePiece
47
-
48
- def initialize(names)
49
- @instructions = 'You can select spots on the board by inputting the row' \
50
- " and column with a comma in between. See example below\n1, 1\n"
51
- super(ChessBoard, @instructions, names)
52
- end
53
-
54
- def to_s
55
- super('chess')
56
- end
57
-
58
- private
59
-
60
- def valid_piece?(piece)
61
- !piece.nil? && piece.owner == @turn
62
- end
63
-
64
- def play_turn
65
- puts "#{@turn}'s turn"
66
- killed = play_move
67
- @winner = @turn if killed.is_a?(King)
68
- change_turn
69
- end
70
-
71
- def setup_board(board)
72
- board.new(@players[0], @players[1])
73
- end
74
- end
75
-
76
83
  class Pawn < BoardgameEngine::Piece
77
84
  def initialize(owner, front)
78
85
  super(owner, 'p')
@@ -86,7 +93,7 @@ module SampleChess
86
93
  # @param [Array<Integer, Integer>] end_location the intended destination
87
94
  # @param [ChessBoard] board the chess board
88
95
  #
89
- # @return [<Type>] whether the pawn can move to the intended destination
96
+ # @return [Boolean] whether the pawn can move to the intended destination
90
97
  def valid_move?(start_location, end_location, board)
91
98
  row, col = start_location
92
99
  end_row, end_col = end_location
@@ -151,12 +158,9 @@ module SampleChess
151
158
  end
152
159
 
153
160
  def valid_move?(start_location, end_location, board)
154
- row, col = start_location
155
- end_row, end_col = end_location
156
-
157
- clear_diag_path?(row, col, end_row, end_col, board) \
158
- || clear_horz_path?(row, col, end_row, end_col, board) \
159
- || clear_vert_path?(row, col, end_row, end_col, board)
161
+ board.clear_diag_path?(start_location, end_location) \
162
+ || board.clear_horz_path?(start_location, end_location) \
163
+ || board.clear_vert_path?(start_location, end_location)
160
164
  end
161
165
  end
162
166
 
@@ -169,8 +173,8 @@ module SampleChess
169
173
  row, col = start_location
170
174
  end_row, end_col = end_location
171
175
 
172
- clear_horz_path?(row, col, end_row, end_col, board) \
173
- || clear_vert_path?(row, col, end_row, end_col, board)
176
+ board.clear_horz_path?(row, col, end_row, end_col, board) \
177
+ || board.clear_vert_path?(row, col, end_row, end_col, board)
174
178
  end
175
179
  end
176
180
 
@@ -183,7 +187,7 @@ module SampleChess
183
187
  row, col = start_location
184
188
  end_row, end_col = end_location
185
189
 
186
- clear_diag_path?(row, col, end_row, end_col, board)
190
+ board.clear_diag_path?(row, col, end_row, end_col, board)
187
191
  end
188
192
  end
189
193
 
@@ -198,9 +202,9 @@ module SampleChess
198
202
 
199
203
  return false unless (row - end_row).abs == 1 && (col - end_col).abs == 1
200
204
 
201
- clear_diag_path?(row, col, end_row, end_col, board) \
202
- || clear_horz_path?(row, col, end_row, end_col, board) \
203
- || clear_vert_path?(row, col, end_row, end_col, board)
205
+ board.clear_diag_path?(row, col, end_row, end_col, board) \
206
+ || board.clear_horz_path?(row, col, end_row, end_col, board) \
207
+ || board.clear_vert_path?(row, col, end_row, end_col, board)
204
208
  end
205
209
  end
206
210
 
@@ -4,8 +4,33 @@ require 'boardgame_engine/boardgame'
4
4
  require 'boardgame_engine/game_modules'
5
5
  require 'boardgame_engine/board_modules'
6
6
 
7
- module SampleConnect4
8
- class Connect4Board < BoardgameEngine::Board
7
+ # module for playing a game of Connect-4
8
+ module Connect4
9
+ # A game of Connect-4
10
+ class Game < BoardgameEngine::Game
11
+ include Games::CyclicalTurn
12
+
13
+ PLAY_INSTRUCTIONS = 'You can select which column to drop you chip into by' \
14
+ ' typing in the row number.'
15
+ GAME_NAME = 'Connect-4'
16
+ NUM_PLAYERS = 2
17
+
18
+ def initialize(names)
19
+ super(Board, names)
20
+ end
21
+
22
+ private
23
+
24
+ def play_turn
25
+ puts "#{@turn}'s turn\nChoose a column to drop your chip in"
26
+ col = get_valid_board_input.to_i
27
+ @board.drop_chip(col, @turn)
28
+ @winner = @turn if @board.consecutive? 4
29
+ end
30
+ end
31
+
32
+ # The Connect-4 board
33
+ class Board
9
34
  include Boards::Grid
10
35
 
11
36
  def initialize
@@ -16,6 +41,12 @@ module SampleConnect4
16
41
  super(show_col: true)
17
42
  end
18
43
 
44
+ # Drop a chip from a certain player into a given column
45
+ #
46
+ # @param [Integer] col The column chosen by the player
47
+ # @param [Player] owner the player dropping the chip
48
+ #
49
+ # @return [void]
19
50
  def drop_chip(col, owner)
20
51
  @board.reverse_each do |row|
21
52
  if row[col].nil?
@@ -29,29 +60,4 @@ module SampleConnect4
29
60
  super(input, only_col: true)
30
61
  end
31
62
  end
32
-
33
- class Connect4 < BoardgameEngine::Boardgame
34
- include Games::CyclicalTurn
35
-
36
- @instructions = 'You can select which column to drop you chip into by' \
37
- ' typing in the row number.'
38
-
39
- def initialize(names)
40
- super(Connect4Board, @instructions, names)
41
- end
42
-
43
- def to_s
44
- super('connect-four')
45
- end
46
-
47
- private
48
-
49
- def play_turn
50
- puts "#{@turn}'s turn\nChoose a column to drop your chip in"
51
- col = get_valid_board_input.to_i
52
- @board.drop_chip(col, @turn)
53
- @winner = @turn if @board.consecutive?
54
- change_turn
55
- end
56
- end
57
63
  end
@@ -18,12 +18,12 @@ module Games
18
18
  # @return [void]
19
19
  def play_move
20
20
  puts 'Select your piece'
21
- piece, row, col = select_piece_from_input
21
+ piece, start_loc = select_piece_from_input
22
22
  puts "Select where to move \"#{piece}\" to. Type \"back\" to reselect piece"
23
- dest = select_destination(piece, row, col)
24
- return play_move if dest == 'back'
23
+ end_loc = select_destination(piece, start_loc)
24
+ return play_move if end_loc == 'back'
25
25
 
26
- @board.move_piece([row, col], dest)
26
+ @board.move_piece(start_loc, end_loc)
27
27
  end
28
28
 
29
29
  private
@@ -45,7 +45,7 @@ module Games
45
45
 
46
46
  # Select a location on the board to move to from user input
47
47
  #
48
- # @param [<Type>] piece The piece to move
48
+ # @param [Piece] piece The piece to move
49
49
  # @param [<Type>] start_location the starting location of the piece
50
50
  #
51
51
  # @return [<Type>] the location of the piece or the string literal 'back'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BoardgameEngine
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boardgame_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - FortHoney
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-17 00:00:00.000000000 Z
11
+ date: 2023-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec