boardgame_engine 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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