connect_4_2307_nick_joop 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 712c64859e2aca132163b360976f53340762dda3a965207d4efd99fa299c7779
4
+ data.tar.gz: 563da52410152fb429a607d9ed745bf80c049f4aab155240b84922af41766ec9
5
+ SHA512:
6
+ metadata.gz: 07da7340c19e38a0f557f4923626f98e8a57b6a34038daa79128cfab2c9b4bd3b7b5924660f0fe83cf25b61be89227ed927b4ca1380c209032911c5f91c2ee5f
7
+ data.tar.gz: 70ea227a35e4a63244fb5630284a74a53822e53198ce720d425d9d2e5000d692fde671f30d6ebaef81df084bb0173cf7cec1aa7cab20e85f44b95c42cf771d6a
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ruby2d'
3
+ require 'require_helper'
4
+
5
+ set width: 775
6
+ set height: 800
7
+ passed_menu = false
8
+
9
+ # cordinate data for rendering blank squares, as well as X's and O's when placed
10
+ # symbols correspond to the return from Player.place_piece and TurnManager.cpu_turn
11
+ cordinates = {
12
+ A5: [50, 700], A4: [50, 600], A3: [50, 500], A2: [50, 400], A1: [50, 300], A0: [50, 200],
13
+ B5: [150, 700], B4: [150, 600], B3: [150, 500], B2: [150, 400], B1: [150, 300], B0: [150, 200],
14
+ C5: [250, 700], C4: [250, 600], C3: [250, 500], C2: [250, 400], C1: [250, 300], C0: [250, 200],
15
+ D5: [350, 700], D4: [350, 600], D3: [350, 500], D2: [350, 400], D1: [350, 300], D0: [350, 200],
16
+ E5: [450, 700], E4: [450, 600], E3: [450, 500], E2: [450, 400], E1: [450, 300], E0: [450, 200],
17
+ F5: [550, 700], F4: [550, 600], F3: [550, 500], F2: [550, 400], F1: [550, 300], F0: [550, 200],
18
+ G5: [650, 700], G4: [650, 600], G3: [650, 500], G2: [650, 400], G1: [650, 300], G0: [650, 200]
19
+
20
+ }
21
+
22
+ def set_up_game_manager
23
+ board = Board.new
24
+ board.setup_game
25
+ player_1 = Player.new("X")
26
+ cpu = ComputerSmart.new(player_1.type)
27
+ TurnManager.new(board, player_1, cpu)
28
+ end
29
+
30
+ game = set_up_game_manager
31
+
32
+ # Uses cordinates Hash to render blank square positions representing connect 4 board
33
+ def render_game(cordinates, board)
34
+ set title: "Connect 4"
35
+ set background: "blue"
36
+ cordinates.each do |key, values|
37
+ Square.new(
38
+ x: values[0], y: values[1],
39
+ size: 85,
40
+ color: 'white', opacity: 1,
41
+ z: 0
42
+ )
43
+ end
44
+ end
45
+
46
+ # Helper method returns the corresponding symbol for selected column to drop token
47
+ def find_column(event)
48
+ return :A if event.x > 0 && event.x <= 140
49
+ return :B if event.x > 140 && event.x <= 240
50
+ return :C if event.x > 240 && event.x <= 340
51
+ return :D if event.x > 340 && event.x <= 440
52
+ return :E if event.x > 440 && event.x <= 540
53
+ return :F if event.x > 540 && event.x <= 640
54
+ return :G if event.x > 640 && event.x <= 800
55
+ end
56
+
57
+ # method gathers cordinates token is placed at, and uses cordinates hash to render
58
+ # token in the correct placement
59
+ def render_token(cords, type, cordinates)
60
+ Text.new(
61
+ "#{type}",
62
+ x: cordinates[cords][0] + 5, y: cordinates[cords][1] -10,
63
+ style: 'bold',
64
+ size: 85,
65
+ color: 'red'
66
+ )
67
+ end
68
+
69
+ # Images used for showing a game over message on screen
70
+ def render_game_over
71
+ Text.new(
72
+ "Game Over",
73
+ x: 125, y: 0,
74
+ style: 'bold',
75
+ size: 100,
76
+ color: 'red'
77
+ )
78
+ Text.new(
79
+ "press p to play again, or q to quit",
80
+ x: 130, y: 125,
81
+ size: 35,
82
+ color: 'red'
83
+ )
84
+ end
85
+
86
+ # key down and mouse down methods for game selection and token position selection
87
+ on :key_down do |event|
88
+ if event.key == "p"
89
+ clear
90
+ game.reset_game
91
+ render_game(cordinates, game.board)
92
+ passed_menu = true
93
+ end
94
+ if event.key == "q"
95
+ close
96
+ end
97
+ end
98
+
99
+ on :mouse_down do |event|
100
+ if passed_menu
101
+ checker = CheckerCriteria.new
102
+ column = find_column(event)
103
+ token_placement = game.board.place_piece(column, game.player.type)
104
+ if !(token_placement == "invalid column, column full")
105
+ render_token(token_placement, game.player.type, cordinates)
106
+ cpu_token = game.cpu_turn
107
+ render_token(cpu_token, game.cpu.type, cordinates)
108
+ game.board.render_board
109
+ if checker.check_win_conditions(game.board) || game.board.draw?
110
+ game.board.clear_board
111
+ game.board.setup_game
112
+ render_game_over
113
+ passed_menu = false
114
+ end
115
+ end
116
+ end
117
+ end
118
+ # key down and mouse down methods for game selection and token position selection
119
+
120
+ # set up the first start screen with p to play and q to quit options
121
+ def render_start_screen
122
+ Text.new(
123
+ "Welcome to connect 4",
124
+ x: 125, y: 325,
125
+ style: 'bold',
126
+ size: 50,
127
+ color: 'red'
128
+ )
129
+ Text.new(
130
+ "press p to play again, or q to quit",
131
+ x: 130, y: 475,
132
+ size: 35,
133
+ color: 'red'
134
+ )
135
+ end
136
+
137
+ render_start_screen
138
+ # set up the first start screen with p to play and q to quit options
139
+
140
+ show
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'require_helper'
3
+
4
+ board = Board.new
5
+ game = GameManager.new
6
+ game.start_game(board)
data/lib/board.rb ADDED
@@ -0,0 +1,98 @@
1
+ require_relative 'token'
2
+
3
+ class Board
4
+ attr_reader :header, :columns
5
+ attr_accessor :play_area
6
+
7
+ def initialize
8
+ @play_area = {
9
+ A: [],
10
+ B: [],
11
+ C: [],
12
+ D: [],
13
+ E: [],
14
+ F: [],
15
+ G: []
16
+ }
17
+ @columns = @play_area.keys
18
+ end
19
+
20
+ def setup_game
21
+ @play_area.values.each do |column|
22
+ 6.times {column.append(Token.new)}
23
+ end
24
+ end
25
+
26
+ def read_cell(column, row)
27
+ row -= 1
28
+ @play_area[column][row].type
29
+ end
30
+
31
+ def render_board
32
+ puts @play_area.keys.join
33
+ board_array = @play_area.values.transpose
34
+ rows = board_array.map {|row| row.map {|token| token.type}}
35
+ rows.each {|line| puts line.join}
36
+ end
37
+
38
+ def clear_board
39
+ initialize
40
+ end
41
+
42
+ def place_piece(column, piece_type)
43
+ return "invalid column" if !@columns.include?(column)
44
+ i = -1
45
+ loop do
46
+ if @play_area[column][i].type == "."
47
+ @play_area[column][i].type = piece_type
48
+ cord = "#{column}#{i+6}"
49
+ return cord.intern
50
+ end
51
+ i -= 1
52
+ return "invalid column, column full" if i < -6
53
+ end
54
+ end
55
+
56
+ def draw? #allways call win condition test first
57
+ all_tokens = @play_area.values.flatten
58
+ return false if all_tokens.any? {|token| token.type == "."}
59
+ true
60
+ end
61
+
62
+ def set_draw_test
63
+ @play_area.each {|k,v| v.each {|token| token.type = "X"}}
64
+ end
65
+ # test if render_board outputs correct rows/columns
66
+ # by comparing to manually setting rows/columns
67
+ # def render_board_manual
68
+ # puts @play_area.keys.join
69
+ # i = 0
70
+ # rows = 5
71
+ # until i > rows do
72
+ # row = form_row(i)
73
+ # puts row
74
+ # i += 1
75
+ # end
76
+ # end
77
+
78
+ # def form_row(num)
79
+ # [@play_area[:A][num].type, @play_area[:B][num].type,
80
+ # @play_area[:C][num].type, @play_area[:D][num].type,
81
+ # @play_area[:E][num].type, @play_area[:F][num].type,
82
+ # @play_area[:G][num].type].join
83
+ # end
84
+
85
+ # def token_positions_by_type(type)
86
+ # counter = 0
87
+ # single_array =[]
88
+ # until counter == 7
89
+ # @play_area[@columns[counter]].select.with_index do |point, index|
90
+ # if point.type == type
91
+ # single_array << "#{@columns[counter].to_s}#{index}"
92
+ # end #if
93
+ # end #do
94
+ # counter += 1
95
+ # end #until
96
+ # single_array
97
+ # end #def
98
+ end
@@ -0,0 +1,110 @@
1
+ class CheckerCriteria
2
+ attr_reader :win, :winner
3
+
4
+ def initialize
5
+ @win = false
6
+ @winner = nil
7
+ @positive_diagonals = [["A5", "B4", "C3", "D2"],
8
+ ["A4", "B3", "C2", "D1"],
9
+ ["A3", "B2", "C1", "D0"],
10
+ ["B5", "C4", "D3", "E2"],
11
+ ["B4", "C3", "D2", "E1"],
12
+ ["B3", "C2", "D1", "E0"],
13
+ ["C5", "D4", "E3", "F2"],
14
+ ["C4", "D3", "E2", "F1"],
15
+ ["C3", "D2", "E1", "F0"],
16
+ ["D5", "E4", "F3", "G2"],
17
+ ["D4", "E3", "F2", "G1"],
18
+ ["D3", "E2", "F1", "G0"]]
19
+
20
+ @negative_diagonals =
21
+ [["D5", "C4", "B3", "A2"],
22
+ ["D4", "C3", "B2", "A1"],
23
+ ["D3", "C2", "B1", "A0"],
24
+ ["E5", "D4", "C3", "B2"],
25
+ ["E4", "D3", "C2", "B1"],
26
+ ["E3", "D2", "C1", "B0"],
27
+ ["F5", "E4", "D3", "C2"],
28
+ ["F4", "E3", "D2", "C1"],
29
+ ["F3", "E2", "D1", "C0"],
30
+ ["G5", "F4", "E3", "D2"],
31
+ ["G4", "F3", "E2", "D1"],
32
+ ["G3", "F2", "E1", "D0"]]
33
+
34
+ @columns = [:A, :B, :C, :D, :E, :F, :G]
35
+ end
36
+
37
+ def check_win_conditions(board)
38
+ check_vertical_win(board) || check_horizontal_win(board) || test_for_diagonal_wins(board)
39
+ end
40
+
41
+ #Private
42
+
43
+ def token_positions_by_type(type, board)
44
+ counter = 0
45
+ single_array =[]
46
+ until counter == 7
47
+ board.play_area[@columns[counter]].select.with_index do |point, index|
48
+ if point.type == type
49
+ single_array << "#{@columns[counter].to_s}#{index}"
50
+ end #if
51
+ end #do
52
+ counter += 1
53
+ end #until
54
+ single_array
55
+ end #def
56
+
57
+ def test_for_diagonal_wins(board)
58
+ @positive_diagonals.each do |win| if (win - token_positions_by_type("X", board)).empty?
59
+ @win = true
60
+ @winner = "X"
61
+ end
62
+ end
63
+ @negative_diagonals.each do |win| if (win - token_positions_by_type("X", board)).empty?
64
+ @win = true
65
+ @winner = "X"
66
+ end
67
+ end
68
+ @positive_diagonals.each do |win| if (win - token_positions_by_type("O", board)).empty?
69
+ @win = true
70
+ @winner = "O"
71
+ end
72
+ end
73
+ @negative_diagonals.each do |win| if (win - token_positions_by_type("O", board)).empty?
74
+ @win = true
75
+ @winner = "O"
76
+ end
77
+ end
78
+ @win
79
+ end
80
+
81
+ def check_vertical_win(board)
82
+ vert_strings = board.play_area.values.map do |column|
83
+ column.map {|token| token.type}
84
+ end
85
+ vert_wins = vert_strings.map {|col| col.join}
86
+ if vert_wins.any? { |str| str.include?("XXXX") }
87
+ @winner = "X"
88
+ @win = true
89
+ elsif vert_wins.any? { |str| str.include?("OOOO") }
90
+ @winner = "O"
91
+ @win = true
92
+ end
93
+ end
94
+
95
+ def check_horizontal_win(board)
96
+ horizontal_strings = board.play_area.values.transpose.map do |row|
97
+ row.map {|token| token.type}
98
+ end
99
+ horizontal_wins = horizontal_strings.map {|row| row.join}
100
+ if horizontal_wins.any? {|str| str.include?("XXXX")}
101
+ @winner = "X"
102
+ @win = true
103
+ elsif horizontal_wins.any? {|str| str.include?("OOOO")}
104
+ @winner = "O"
105
+ @win = true
106
+ end
107
+ end
108
+ end
109
+
110
+
@@ -0,0 +1,16 @@
1
+ class ComputerRandom
2
+ attr_reader :type, :enemy
3
+ def initialize(enemy_type = "X")
4
+ @type = OPPONENT[enemy_type]
5
+ @enemy = enemy_type
6
+ end
7
+
8
+ OPPONENT = {"O"=>"X", "X"=>"O"}
9
+ COLUMNS = [:A, :B, :C, :D, :E, :F, :G]
10
+
11
+ def place_piece(board)
12
+ board.place_piece(COLUMNS[rand(0..6)], @type)
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,83 @@
1
+ class ComputerSmart
2
+ attr_reader :type, :enemy, :main_connected_tokens, :placement_column
3
+
4
+ def initialize(type = "X") #default needs to be removed eventually
5
+ @type = OPPONENT[type]
6
+ @enemy = type
7
+ @main_connected_tokens = 0
8
+ @placement_column = nil
9
+ @blocking = false
10
+ end
11
+
12
+ OPPONENT = {"O"=>"X", "X"=>"O"}
13
+ COLUMNS_SYM = [:A, :B, :C, :D, :E, :F, :G]
14
+ WEIGHTED_RANDOM = [:A, :A, :B, :B, :C, :C, :C, :C, :D, :D, :D, :E, :E, :E, :E, :F, :F, :G, :G]
15
+
16
+ def place_piece(board)
17
+ @main_connected_tokens = 0
18
+ tester(board, @enemy, false)
19
+ tester(board, @type, true)
20
+ case @main_connected_tokens
21
+ when 2..6
22
+ board.place_piece(COLUMNS_SYM[@placement_column], @type)
23
+ else
24
+ board.place_piece(WEIGHTED_RANDOM[rand(0..18)], @type)
25
+ end
26
+ end
27
+
28
+ # Private
29
+
30
+ def tester(board, type_checker, blocking)
31
+ [*0..6].each do |column_number|
32
+ compass_tester(column_number, board, 1, -1, type_checker, blocking) #checks positive diagonal
33
+ compass_tester(column_number, board, -1, -1, type_checker, blocking) #checks negative diagonal
34
+ compass_tester(column_number, board, 1, 0, type_checker, blocking) #checks horizontal
35
+ compass_tester(column_number, board, 0, -1, type_checker, blocking) #checks vertical
36
+ end
37
+ end
38
+
39
+ def compass_tester(column_number, board, array_number_num, item_number_num, type_checker, block)
40
+ connected_tokens = 0
41
+ array_number = column_number
42
+ item_number = find_empty(column_number, board)
43
+ rev_array_number = column_number
44
+ rev_item_number = find_empty(column_number, board)
45
+ loop do
46
+ array_number += array_number_num
47
+ item_number += item_number_num
48
+ break if array_number > 6 || array_number < 0
49
+ break if item_number > 5 || item_number < 0
50
+ break if board.play_area[COLUMNS_SYM[array_number]][item_number].type == "."
51
+ break if board.play_area[COLUMNS_SYM[array_number]][item_number].type == type_checker
52
+ connected_tokens += 1
53
+ end
54
+ loop do
55
+ rev_array_number += array_number_num * -1
56
+ rev_item_number += item_number_num * -1
57
+ break if rev_array_number > 6 || rev_array_number < 0
58
+ break if rev_item_number > 5 || rev_item_number < 0
59
+ break if board.play_area[COLUMNS_SYM[rev_array_number]][rev_item_number].type == "."
60
+ break if board.play_area[COLUMNS_SYM[rev_array_number]][rev_item_number].type == type_checker
61
+ connected_tokens += 1
62
+ end
63
+ if connected_tokens > @main_connected_tokens && find_empty(column_number, board) < 6
64
+ @main_connected_tokens = connected_tokens
65
+ @placement_column = column_number
66
+ @blocking = block
67
+ end
68
+ end
69
+
70
+ def find_empty(column_number, board)
71
+ vertical_position = 5
72
+ previous = 6
73
+ loop do
74
+ if board.play_area[COLUMNS_SYM[column_number]][vertical_position].type == "."
75
+ return previous = vertical_position
76
+ end
77
+ vertical_position -= 1
78
+ break if vertical_position <= -1
79
+ end
80
+ previous
81
+ end
82
+
83
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'require_helper'
2
+
3
+ class GameManager
4
+
5
+ def start_game(board)
6
+ puts "Welcome to CONNECT FOUR"
7
+ puts "Enter p to play. Enter q to quit"
8
+ choice = gets.chomp
9
+
10
+ if choice.downcase == "p"
11
+ selection = ''
12
+ player_1 = x_or_o
13
+ cpu = select_cpu_type(player_1)
14
+ board.setup_game
15
+ game = TurnManager.new(board, player_1, cpu)
16
+ game_loop(board, game, cpu)
17
+ elsif choice.downcase == "q"
18
+ return false
19
+ else
20
+ puts "Invalid choice"
21
+ start_game(board)
22
+ end
23
+ end
24
+
25
+ def game_loop(board, game, cpu)
26
+ checker = CheckerCriteria.new
27
+ loop do
28
+ game.start_turn
29
+ break if check_re_render(board, checker)
30
+ game.cpu_turn
31
+ break if check_re_render(board, checker)
32
+ if board.draw?
33
+ puts "It's a draw"
34
+ board.render_board
35
+ board.clear_board
36
+ break
37
+ end
38
+ end
39
+ start_game(board)
40
+ end
41
+
42
+ def select_cpu_type(player)
43
+ puts "s for smart computer, r for random computer"
44
+ choice = gets.chomp
45
+ if choice == 's'
46
+ cpu = ComputerSmart.new(player.type)
47
+ elsif choice == 'r'
48
+ cpu = ComputerRandom.new(player.type)
49
+ else
50
+ puts "invalid selection"
51
+ select_cpu_type(player)
52
+ end
53
+ end
54
+
55
+ def check_re_render(board, checker)
56
+ if checker.check_win_conditions(board)
57
+ board.render_board
58
+ board.clear_board
59
+ puts "Game Over: #{checker.winner} wins"
60
+ return true
61
+ end
62
+ false
63
+ end
64
+
65
+ def x_or_o
66
+ puts "would you like X's or O's?('x/o')"
67
+ selection = gets.chomp
68
+ if selection == "x"
69
+ return player = Player.new("X")
70
+ elsif selection == "o"
71
+ return player = Player.new("O")
72
+ else
73
+ puts "invalid selecton"
74
+ x_or_o
75
+ end
76
+ end
77
+
78
+ end
data/lib/player.rb ADDED
@@ -0,0 +1,19 @@
1
+ class Player
2
+ attr_reader :type
3
+ def initialize(type)
4
+ @type = find_type(type)
5
+ end
6
+
7
+ def find_type(type)
8
+ case type
9
+ when 2, "x", "X"
10
+ "X"
11
+ when 1, "o", "O", 0
12
+ "O"
13
+ end
14
+ end
15
+
16
+ def place_piece(column, board)
17
+ board.place_piece(column, @type)
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'board'
2
+ require_relative 'token'
3
+ require_relative 'player'
4
+ require_relative 'turn_manager'
5
+ require_relative 'computer_random'
6
+ require_relative 'checker_criteria'
7
+ require_relative 'game_manager.rb'
8
+ require_relative 'computer_smart'
data/lib/token.rb ADDED
@@ -0,0 +1,7 @@
1
+ class Token
2
+ attr_accessor :type
3
+
4
+ def initialize
5
+ @type = "."
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ class TurnManager
2
+ attr_reader :board, :player, :cpu
3
+ def initialize(board, player, cpu)
4
+ @board = board
5
+ @player = player
6
+ @cpu = cpu
7
+ end
8
+
9
+ def start_turn
10
+ @board.render_board
11
+ puts "Select a column for your piece(A-G)"
12
+ column = gets.chomp.upcase.intern
13
+ new_token = @player.place_piece(column, @board)
14
+ check_column(new_token)
15
+ end
16
+
17
+ def check_column(new_token)
18
+ if new_token == "invalid column, column full" || new_token == "invalid column"
19
+ until new_token != "invalid column, column full" && new_token != "invalid column"
20
+ puts "#{new_token}"
21
+ puts "Select another row(A-G)"
22
+ column = gets.chomp.upcase.intern
23
+ new_token = @player.place_piece(column, @board)
24
+ end
25
+ end
26
+ end
27
+
28
+ # method for cpu turn here
29
+ def cpu_turn
30
+ new_token = @cpu.place_piece(@board)
31
+ if new_token == "invalid column, column full" || new_token == "invalid column"
32
+ until new_token != "invalid column, column full" && new_token != "invalid column"
33
+ new_token = @cpu.place_piece(@board)
34
+ end
35
+ end
36
+ new_token
37
+ end
38
+
39
+ def reset_game
40
+ @board.clear_board
41
+ @board.setup_game
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: connect_4_2307_nick_joop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Nick Spencer
8
+ - Joop Stark
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-07-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby2d
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.12.1
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.12.1
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.12'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.12'
42
+ description: Connect 4, play either CLI or graphic interface vs computer
43
+ email:
44
+ - strangebrew202@gmail.com
45
+ - stark.joop@gmail.com
46
+ executables:
47
+ - connect_4_graphics
48
+ - connect_4_runner
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - bin/connect_4_graphics
53
+ - bin/connect_4_runner
54
+ - lib/board.rb
55
+ - lib/checker_criteria.rb
56
+ - lib/computer_random.rb
57
+ - lib/computer_smart.rb
58
+ - lib/game_manager.rb
59
+ - lib/player.rb
60
+ - lib/require_helper.rb
61
+ - lib/token.rb
62
+ - lib/turn_manager.rb
63
+ homepage:
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.3.26
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Connect 4 game
86
+ test_files: []