connect_4_2307_nick_joop 0.0.2

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