boardgame_engine 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.
- checksums.yaml +7 -0
- data/.rubocop.yml +16 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +57 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/boardgame_engine.gemspec +36 -0
- data/lib/boardgame_engine/boardgame.rb +144 -0
- data/lib/boardgame_engine/chess/chess.rb +103 -0
- data/lib/boardgame_engine/chess/chess_pieces.rb +164 -0
- data/lib/boardgame_engine/connect4/connect4.rb +74 -0
- data/lib/boardgame_engine/multiplayergame.rb +5 -0
- data/lib/boardgame_engine/version.rb +5 -0
- data/lib/boardgame_engine.rb +12 -0
- data/sig/boardgame_engine.rbs +4 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9e57f58857092b8b08b48de9bafcf09a08297302fcbe695afde86125615d07f1
|
4
|
+
data.tar.gz: c7de6b02681a7b7def284e31186fc61281fe9ae41dfe75464dc0a5e83b5bde9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2155a5ca5a0c871e277a7a0e4beb9ea25b86a17ada30481d4f37b6104385c300095ef61cd694b860cadc72ab9230f6f7d199934a15c96de3278b22756174b07c
|
7
|
+
data.tar.gz: accc0b89071a919f466be01d1ecd68bc4f2fe3096817ff6340b161e731beabe1e05142399af352922cf7ac521ee95711fc6681068cf5b083c3e7ba9fb51ca02f
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.1
|
3
|
+
|
4
|
+
Style/StringLiterals:
|
5
|
+
Enabled: true
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
|
8
|
+
Style/StringLiteralsInInterpolation:
|
9
|
+
Enabled: true
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
14
|
+
|
15
|
+
Metrics/BlockLength:
|
16
|
+
AllowedMethods: ['describe', 'context']
|
data/CHANGELOG.md
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
boardgame_engine (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
diff-lcs (1.5.0)
|
11
|
+
json (2.6.2)
|
12
|
+
parallel (1.22.1)
|
13
|
+
parser (3.1.2.1)
|
14
|
+
ast (~> 2.4.1)
|
15
|
+
rainbow (3.1.1)
|
16
|
+
rake (13.0.6)
|
17
|
+
regexp_parser (2.6.0)
|
18
|
+
rexml (3.2.5)
|
19
|
+
rspec (3.11.0)
|
20
|
+
rspec-core (~> 3.11.0)
|
21
|
+
rspec-expectations (~> 3.11.0)
|
22
|
+
rspec-mocks (~> 3.11.0)
|
23
|
+
rspec-core (3.11.0)
|
24
|
+
rspec-support (~> 3.11.0)
|
25
|
+
rspec-expectations (3.11.1)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.11.0)
|
28
|
+
rspec-mocks (3.11.1)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.11.0)
|
31
|
+
rspec-support (3.11.1)
|
32
|
+
rubocop (1.36.0)
|
33
|
+
json (~> 2.3)
|
34
|
+
parallel (~> 1.10)
|
35
|
+
parser (>= 3.1.2.1)
|
36
|
+
rainbow (>= 2.2.2, < 4.0)
|
37
|
+
regexp_parser (>= 1.8, < 3.0)
|
38
|
+
rexml (>= 3.2.5, < 4.0)
|
39
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
40
|
+
ruby-progressbar (~> 1.7)
|
41
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
42
|
+
rubocop-ast (1.21.0)
|
43
|
+
parser (>= 3.1.1.0)
|
44
|
+
ruby-progressbar (1.11.0)
|
45
|
+
unicode-display_width (2.3.0)
|
46
|
+
|
47
|
+
PLATFORMS
|
48
|
+
x86_64-linux
|
49
|
+
|
50
|
+
DEPENDENCIES
|
51
|
+
boardgame_engine!
|
52
|
+
rake (~> 13.0)
|
53
|
+
rspec (~> 3.2)
|
54
|
+
rubocop (~> 1.21)
|
55
|
+
|
56
|
+
BUNDLED WITH
|
57
|
+
2.3.22
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# BoardgameEngine
|
2
|
+
|
3
|
+
A gem that provides a template for creating boardgames. It aims to streamline
|
4
|
+
the process of making boardgames to be played on the terminal by providing a
|
5
|
+
variety of methods and classes.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install the gem and add to the application's Gemfile by executing:
|
10
|
+
|
11
|
+
$ bundle add boardgame_engine
|
12
|
+
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
|
+
|
15
|
+
$ gem install boardgame_engine
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
TODO: Write usage instructions here
|
20
|
+
|
21
|
+
## Development
|
22
|
+
|
23
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
24
|
+
|
25
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Forthoney/boardgame_engine.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/boardgame_engine/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "boardgame_engine"
|
7
|
+
spec.version = BoardgameEngine::VERSION
|
8
|
+
spec.authors = ["FortHoney"]
|
9
|
+
spec.email = ["castlehoneyjung@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "A gem that makes digitizing/creating board games to be played on the terminal quick and easy."
|
12
|
+
spec.homepage = "https://github.com/Forthoney/boardgame_engine"
|
13
|
+
spec.required_ruby_version = ">= 2.7.0"
|
14
|
+
|
15
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/Forthoney/boardgame_engine"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/Forthoney/boardgame_engine/blob/main/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
33
|
+
|
34
|
+
# For more information and examples about making a new gem, check out our
|
35
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
36
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# All classes in this script are intended to be abstract, meaning they should
|
4
|
+
# not be called on their own.
|
5
|
+
|
6
|
+
# Class representing a physical board comprised of a grid in a board game. It
|
7
|
+
# acts as both the View and Model if the project were to be compared to a MVC
|
8
|
+
# model. It plays both roles as the board in a board game not only stores data,
|
9
|
+
# but also IS the data that must be shown to the players.
|
10
|
+
class Board
|
11
|
+
attr_reader :board
|
12
|
+
|
13
|
+
def initialize(row, col)
|
14
|
+
@board = Array.new(row) { Array.new(col) { nil } }
|
15
|
+
end
|
16
|
+
|
17
|
+
def display(show_row: false, show_col: false)
|
18
|
+
if show_row
|
19
|
+
@board.each_with_index { |row, idx| puts("#{idx} " + format_row(row)) }
|
20
|
+
else
|
21
|
+
@board.each { |row| puts format_row(row) }
|
22
|
+
end
|
23
|
+
return unless show_col
|
24
|
+
|
25
|
+
column_spacer = show_row ? " " : ""
|
26
|
+
puts(@board[0].each_index.reduce(column_spacer) do |str, idx|
|
27
|
+
str + " #{idx} "
|
28
|
+
end)
|
29
|
+
end
|
30
|
+
|
31
|
+
def move_piece(start_row, start_col, end_row, end_col)
|
32
|
+
piece = @board[start_row][start_col]
|
33
|
+
@board[start_row][start_col] = nil
|
34
|
+
destination = @board[end_row][end_col]
|
35
|
+
@board[end_row][end_col] = piece
|
36
|
+
|
37
|
+
destination
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def format_row(row)
|
43
|
+
row.map { |elem| "[#{elem.nil? ? " " : elem}]" }.join
|
44
|
+
end
|
45
|
+
|
46
|
+
def spot_playable?(piece, row, col)
|
47
|
+
piece.possible_moves.include? [row, col]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Class representing a player in a game
|
52
|
+
class Player
|
53
|
+
attr_reader :name
|
54
|
+
|
55
|
+
def initialize(name)
|
56
|
+
@name = name
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
@name.to_s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Class for running a board game.
|
65
|
+
class Boardgame
|
66
|
+
EXIT_INSTRUCTIONS ||= "Try a sample input or input 'back' to leave the " \
|
67
|
+
"tutorial. Type in 'exit' anytime to exit the game fully"
|
68
|
+
|
69
|
+
def initialize(board, instructions, name1 = "Player 1", name2 = "Player 2")
|
70
|
+
@player1 = Player.new(name1)
|
71
|
+
@player2 = Player.new(name2)
|
72
|
+
@board = setup_board(board)
|
73
|
+
@instructions = instructions
|
74
|
+
@winner = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.play(do_onboarding: true)
|
78
|
+
puts "What is Player 1's name?"
|
79
|
+
player1 = gets.chomp
|
80
|
+
puts "What is Player 2's name?"
|
81
|
+
player2 = gets.chomp
|
82
|
+
@game = new(player1, player2)
|
83
|
+
|
84
|
+
puts "Welcome to #{@game}!"
|
85
|
+
@game.onboarding if do_onboarding
|
86
|
+
puts "Starting #{@game}..."
|
87
|
+
@game.start
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s(game_name = "boardgame")
|
91
|
+
"#{game_name} between #{@player1} and #{@player2}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def onboarding
|
95
|
+
puts "Would you like a tutorial on how to play on this program? \n(y, n)"
|
96
|
+
case gets.chomp
|
97
|
+
when "y"
|
98
|
+
tutorial
|
99
|
+
when "n"
|
100
|
+
puts "Skipping tutorial"
|
101
|
+
else
|
102
|
+
puts 'Please answer either "y" or "n"'
|
103
|
+
onboarding
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def tutorial
|
108
|
+
puts @instructions + Boardgame::EXIT_INSTRUCTIONS
|
109
|
+
input = gets.chomp
|
110
|
+
until input == "back"
|
111
|
+
exit if input == "exit"
|
112
|
+
puts valid_input?(input) ? "Valid input!" : "Invalid input"
|
113
|
+
input = gets.chomp
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def start(turn = @player1)
|
118
|
+
@turn = turn
|
119
|
+
@board.display
|
120
|
+
until @winner
|
121
|
+
play_turn
|
122
|
+
@board.display
|
123
|
+
end
|
124
|
+
puts "#{@winner} wins!"
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
def proper_format_input(special_commands = [])
|
130
|
+
input = gets.chomp
|
131
|
+
until valid_input?(input)
|
132
|
+
exit if input == "exit"
|
133
|
+
return input if special_commands.include?(input)
|
134
|
+
|
135
|
+
puts "Input is in the wrong format or out of bounds. Try again"
|
136
|
+
input = gets.chomp
|
137
|
+
end
|
138
|
+
input
|
139
|
+
end
|
140
|
+
|
141
|
+
def setup_board(board)
|
142
|
+
board.new
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/boardgame_engine"
|
4
|
+
require "./lib/boardgame_engine/chess/chess_pieces"
|
5
|
+
|
6
|
+
class ChessBoard < Board
|
7
|
+
attr_reader :board
|
8
|
+
|
9
|
+
def initialize(player1, player2)
|
10
|
+
super(8, 8)
|
11
|
+
setup(player1, player2)
|
12
|
+
end
|
13
|
+
|
14
|
+
def display
|
15
|
+
super(show_row: true, show_col: true)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def setup(player1, player2)
|
21
|
+
set_pawns(player1, player2)
|
22
|
+
set_non_pawns(player1, player2)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_pawns(player1, player2)
|
26
|
+
@board[1] = @board[1].map { Pawn.new(player1, 1) }
|
27
|
+
@board[-2] = @board[-2].map { Pawn.new(player2, -1) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_non_pawns(player1, player2)
|
31
|
+
pieces = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook]
|
32
|
+
pieces.each_with_index do |piece, idx|
|
33
|
+
@board[0][idx] = piece.new(player1)
|
34
|
+
@board[7][7 - idx] = piece.new(player2)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Chess < Boardgame
|
40
|
+
include TwoPlayers
|
41
|
+
|
42
|
+
def initialize(name1 = "Player 1", name2 = "Player 2")
|
43
|
+
@instructions = "You can select spots on the board by inputting the row " \
|
44
|
+
"and column with a comma in between. See example below\n1, 1\n"
|
45
|
+
super(ChessBoard, @instructions, name1, name2)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
super("chess")
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def valid_input?(input)
|
55
|
+
coords = input.split(",")
|
56
|
+
coords.all? { |c| c.match?(/[[:digit:]]/) && c.to_i.between?(0, 7) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_piece?(piece)
|
60
|
+
!piece.nil? && piece.owner == @turn
|
61
|
+
end
|
62
|
+
|
63
|
+
def select_piece
|
64
|
+
input = proper_format_input
|
65
|
+
input.split(",").map(&:to_i) => [row, col]
|
66
|
+
if valid_piece? @board.board.dig(row, col)
|
67
|
+
[row, col]
|
68
|
+
else
|
69
|
+
puts "Invalid piece. Try again"
|
70
|
+
select_piece
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def select_destination(piece, row, col)
|
75
|
+
input = proper_format_input(["back"])
|
76
|
+
return "back" if input == "back"
|
77
|
+
|
78
|
+
input.split(",").map(&:to_i) => [end_row, end_col]
|
79
|
+
if piece.valid_move?(row, col, end_row, end_col, @board.board)
|
80
|
+
[end_row, end_col]
|
81
|
+
else
|
82
|
+
puts "Invalid destination. Try again"
|
83
|
+
select_destination(piece, row, col)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def play_turn
|
88
|
+
puts "#{@turn}'s turn\nSelect your piece"
|
89
|
+
select_piece => [row, col]
|
90
|
+
piece = @board.board[row][col]
|
91
|
+
puts "Select where to move #{piece} to. Type back to reselect piece"
|
92
|
+
dest = select_destination(piece, row, col)
|
93
|
+
return if dest == "back"
|
94
|
+
|
95
|
+
killed = @board.move_piece(row, col, dest[0], dest[1])
|
96
|
+
@winner = piece.owner if killed.is_a?(King)
|
97
|
+
change_turn
|
98
|
+
end
|
99
|
+
|
100
|
+
def setup_board(board)
|
101
|
+
board.new(@player1, @player2)
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChessPiece
|
4
|
+
attr_accessor :status
|
5
|
+
attr_reader :owner
|
6
|
+
|
7
|
+
def initialize(owner, name)
|
8
|
+
@kill_log = []
|
9
|
+
@status = "alive"
|
10
|
+
@owner = owner
|
11
|
+
@name = name
|
12
|
+
end
|
13
|
+
|
14
|
+
def kill(other)
|
15
|
+
@kill_log.push(other)
|
16
|
+
other.status = "dead"
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@name.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def valid_diag_move?(row, col, end_row, end_col, board)
|
26
|
+
((end_row - row).abs == (end_col - col).abs) \
|
27
|
+
&& clear_path?(row, col, end_row, end_col, board)
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_horz_move?(row, col, end_row, end_col, board)
|
31
|
+
(end_row == row) && clear_path?(row, col, end_row, end_col, board)
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_vert_move?(row, col, end_row, end_col, board)
|
35
|
+
(end_col == col) && clear_path?(row, col, end_row, end_col, board)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def next_cell(row, col, end_row, end_col)
|
41
|
+
row_move = 0
|
42
|
+
col_move = 0
|
43
|
+
|
44
|
+
col_move = (end_col - col) / (end_col - col).abs if end_col != col
|
45
|
+
row_move = (end_row - row) / (end_row - row).abs if end_row != row
|
46
|
+
|
47
|
+
[row + row_move, col + col_move]
|
48
|
+
end
|
49
|
+
|
50
|
+
def clear_path?(row, col, end_row, end_col, board)
|
51
|
+
current_tile = board.dig(row, col)
|
52
|
+
if (row == end_row) && (col == end_col)
|
53
|
+
current_tile.nil? || (current_tile.owner != @owner)
|
54
|
+
elsif current_tile.nil? || current_tile.equal?(self)
|
55
|
+
next_cell(row, col, end_row, end_col) => [next_row, next_col]
|
56
|
+
clear_path?(next_row, next_col, end_row, end_col, board)
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Pawn < ChessPiece
|
64
|
+
def initialize(owner, front)
|
65
|
+
super(owner, "p")
|
66
|
+
@first_move = true
|
67
|
+
@front = front
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_move?(row, col, end_row, end_col, board)
|
71
|
+
return false unless valid_forward_move?(row, end_row)
|
72
|
+
|
73
|
+
if col == end_col # only forward
|
74
|
+
valid_dest = board.dig(end_row, end_col).nil?
|
75
|
+
@first_move = false if valid_dest
|
76
|
+
return valid_dest
|
77
|
+
elsif (col - end_col).abs == 1 # diagonal movement
|
78
|
+
other_piece = board.dig(end_row, end_col)
|
79
|
+
return other_piece && (other_piece.owner != @owner)
|
80
|
+
end
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def valid_forward_move?(row, end_row)
|
87
|
+
if @first_move
|
88
|
+
(row + @front * 2 == end_row) || (row + @front == end_row)
|
89
|
+
else
|
90
|
+
row + @front == end_row
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Queen < ChessPiece
|
96
|
+
def initialize(owner)
|
97
|
+
super(owner, "Q")
|
98
|
+
end
|
99
|
+
|
100
|
+
def valid_move?(row, col, end_row, end_col, board)
|
101
|
+
valid_diag_move?(row, col, end_row, end_col, board) \
|
102
|
+
|| valid_horz_move?(row, col, end_row, end_col, board) \
|
103
|
+
|| valid_vert_move?(row, col, end_row, end_col, board)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Rook < ChessPiece
|
108
|
+
def initialize(owner)
|
109
|
+
super(owner, "R")
|
110
|
+
end
|
111
|
+
|
112
|
+
def valid_move?(row, col, end_row, end_col, board)
|
113
|
+
valid_horz_move?(row, col, end_row, end_col, board) \
|
114
|
+
|| valid_vert_move?(row, col, end_row, end_col, board)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Bishop < ChessPiece
|
119
|
+
def initialize(owner)
|
120
|
+
super(owner, "B")
|
121
|
+
end
|
122
|
+
|
123
|
+
def valid_move?(row, col, end_row, end_col, board)
|
124
|
+
valid_diag_move?(row, col, end_row, end_col, board)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class King < ChessPiece
|
129
|
+
def initialize(owner)
|
130
|
+
super(owner, "K")
|
131
|
+
end
|
132
|
+
|
133
|
+
def valid_move?(row, col, end_row, end_col, board)
|
134
|
+
return false unless (row - end_row).abs == 1 && (col - end_col).abs == 1
|
135
|
+
|
136
|
+
valid_diag_move?(row, col, end_row, end_col, board) \
|
137
|
+
|| valid_horz_move?(row, col, end_row, end_col, board) \
|
138
|
+
|| valid_vert_move?(row, col, end_row, end_col, board)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class Knight < ChessPiece
|
143
|
+
def initialize(owner)
|
144
|
+
# K was already taken by king, so I had to choose N
|
145
|
+
super(owner, "N")
|
146
|
+
end
|
147
|
+
|
148
|
+
def valid_move?(row, col, end_row, end_col, board)
|
149
|
+
within_movement(row, col, end_row, end_col) \
|
150
|
+
&& not_occupied(end_row, end_col, board)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def within_movement(row, col, end_row, end_col)
|
156
|
+
((row - end_row).abs == 2 and (col - end_col).abs == 1) \
|
157
|
+
|| ((row - end_row).abs == 1 and (col - end_col).abs == 2)
|
158
|
+
end
|
159
|
+
|
160
|
+
def not_occupied(end_row, end_col, board)
|
161
|
+
spot = board.dig(end_row, end_col)
|
162
|
+
spot.nil? || spot.owner != @owner
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/boardgame_engine"
|
4
|
+
|
5
|
+
class Connect4Board < Board
|
6
|
+
def initialize
|
7
|
+
super(6, 7)
|
8
|
+
end
|
9
|
+
|
10
|
+
def display
|
11
|
+
super(show_col: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def drop_chip(col, owner)
|
15
|
+
@board.reverse_each do |row|
|
16
|
+
if row[col].nil?
|
17
|
+
row[col] = owner
|
18
|
+
break
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Connect4 < Boardgame
|
25
|
+
include TwoPlayers
|
26
|
+
|
27
|
+
@instructions = "You can select which column to drop you chip into by" \
|
28
|
+
" typing in the row number."
|
29
|
+
|
30
|
+
def initialize(name1 = "Player 1", name2 = "Player 2")
|
31
|
+
super(Connect4Board, @instructions, name1, name2)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
super("connect-four")
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def valid_input?(input)
|
41
|
+
input.match?(/[[:digit:]]/) && input.to_i.between?(0, 6)
|
42
|
+
end
|
43
|
+
|
44
|
+
def play_turn
|
45
|
+
puts "#{@turn}'s turn. Choose a column to drop your chip in"
|
46
|
+
col = proper_format_input.to_i
|
47
|
+
@board.drop_chip(col, @turn)
|
48
|
+
@winner = @turn if win?
|
49
|
+
change_turn
|
50
|
+
end
|
51
|
+
|
52
|
+
def win?
|
53
|
+
[@board.board,
|
54
|
+
@board.board.transpose,
|
55
|
+
align_diagonally(@board.board),
|
56
|
+
align_diagonally(@board.board.transpose)].each do |config|
|
57
|
+
config.each { |direction| return true if four_in_a_row? direction }
|
58
|
+
end
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def four_in_a_row?(row)
|
63
|
+
counts = row.chunk { |x| x }.map { |x, xs| [x, xs.length] }
|
64
|
+
return true if counts.any? { |x, count| count > 3 && !x.nil? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def align_diagonally(board)
|
68
|
+
board.map.with_index do |row, idx|
|
69
|
+
left_filler = Array.new(board.length - 1 - idx, nil)
|
70
|
+
right_filler = Array.new(idx, nil)
|
71
|
+
left_filler + row + right_filler
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "boardgame_engine/version"
|
4
|
+
|
5
|
+
require_relative "boardgame_engine/boardgame"
|
6
|
+
require_relative "boardgame_engine/multiplayergame"
|
7
|
+
require_relative "boardgame_engine/chess/chess"
|
8
|
+
require_relative "boardgame_engine/connect4/connect4"
|
9
|
+
|
10
|
+
module BoardgameEngine
|
11
|
+
class Error < StandardError; end
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: boardgame_engine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- FortHoney
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-10-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- castlehoneyjung@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".rubocop.yml"
|
35
|
+
- CHANGELOG.md
|
36
|
+
- Gemfile
|
37
|
+
- Gemfile.lock
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- boardgame_engine.gemspec
|
41
|
+
- lib/boardgame_engine.rb
|
42
|
+
- lib/boardgame_engine/boardgame.rb
|
43
|
+
- lib/boardgame_engine/chess/chess.rb
|
44
|
+
- lib/boardgame_engine/chess/chess_pieces.rb
|
45
|
+
- lib/boardgame_engine/connect4/connect4.rb
|
46
|
+
- lib/boardgame_engine/multiplayergame.rb
|
47
|
+
- lib/boardgame_engine/version.rb
|
48
|
+
- sig/boardgame_engine.rbs
|
49
|
+
homepage: https://github.com/Forthoney/boardgame_engine
|
50
|
+
licenses: []
|
51
|
+
metadata:
|
52
|
+
homepage_uri: https://github.com/Forthoney/boardgame_engine
|
53
|
+
source_code_uri: https://github.com/Forthoney/boardgame_engine
|
54
|
+
changelog_uri: https://github.com/Forthoney/boardgame_engine/blob/main/CHANGELOG.md
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.7.0
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.3.7
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: A gem that makes digitizing/creating board games to be played on the terminal
|
74
|
+
quick and easy.
|
75
|
+
test_files: []
|