Skip to content

Instantly share code, notes, and snippets.

@steven-ferguson
Created September 7, 2013 00:46
Show Gist options
  • Save steven-ferguson/6471797 to your computer and use it in GitHub Desktop.
Save steven-ferguson/6471797 to your computer and use it in GitHub Desktop.
class Board
attr_reader :columns, :height, :width
def initialize(width, height)
@columns = []
@height = height
@width = width
width.times do |position|
column = Column.new(position, height)
columns << column
end
end
def winner?(token, win_length)
winning_conditions = [get_vertical_matches(token) + 1,
get_left_matches(token) + get_right_matches(token) + 1,
get_negative_slope_left_diagonal_matches(token) + get_negative_slope_right_diagonal_matches(token) + 1, get_positive_slope_left_diagonal_matches(token) + get_positive_slope_right_diagonal_matches(token) + 1]
winning_conditions.any? do |condition|
condition >= win_length
end
end
private
def get_vertical_matches(token)
consecutive_matches = 0
token.row_number.times do |difference|
if @columns[token.column_number].slots[token.row_number - difference - 1].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
def get_left_matches(token)
consecutive_matches = 0
token.column_number.times do |index|
if @columns[token.column_number - index - 1].slots[token.row_number].nil? || @columns[token.column_number - index - 1].slots[token.row_number].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
def get_right_matches(token)
columns_right = @columns.length - (token.column_number + 1)
# puts "columns right: #{columns_right}"
consecutive_matches = 0
columns_right.times do |difference|
# puts "column val: #{@columns[token.column_number + difference + 1].slots[token.row_number].nil?}"
if @columns[token.column_number + difference + 1].slots[token.row_number].nil? || @columns[token.column_number + difference + 1].slots[token.row_number].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
# puts "right matches: #{consecutive_matches}"
consecutive_matches
end
def get_negative_slope_left_diagonal_matches(token)
columns_to_zero = token.column_number
consecutive_matches = 0
rows_to_top = @height - token.row_number - 1
if columns_to_zero < rows_to_top
diagonals_to_get = columns_to_zero
else
diagonals_to_get = rows_to_top
end
diagonals_to_get.times do |difference|
if @columns[token.column_number - difference - 1].slots[token.row_number + difference + 1].nil? || @columns[token.column_number - difference - 1].slots[token.row_number + difference + 1].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
def get_negative_slope_right_diagonal_matches(token)
consecutive_matches = 0
columns_to_max = @width - token.column_number - 1
rows_to_bottom = token.row_number
diagonals_to_get = columns_to_max < rows_to_bottom ? columns_to_max : rows_to_bottom
diagonals_to_get.times do |difference|
if @columns[token.column_number + difference + 1].slots[token.row_number - difference - 1].nil? || @columns[token.column_number + difference + 1].slots[token.row_number - difference - 1].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
def get_positive_slope_left_diagonal_matches(token)
consecutive_matches = 0
columns_to_zero = token.column_number
rows_to_bottom = token.row_number
if columns_to_zero < rows_to_bottom
diagonals_to_get = columns_to_zero
else
diagonals_to_get = rows_to_bottom
end
diagonals_to_get.times do |difference|
if @columns[token.column_number - difference - 1].slots[token.row_number - difference - 1].nil? || @columns[token.column_number - difference - 1].slots[token.row_number - difference - 1].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
def get_positive_slope_right_diagonal_matches(token)
consecutive_matches = 0
columns_to_max = @width - token.column_number - 1
rows_to_top = @height - token.row_number - 1
diagonals_to_get = columns_to_max < rows_to_top ? columns_to_max : rows_to_top
diagonals_to_get.times do |difference|
if @columns[token.column_number + difference + 1].slots[token.row_number + difference + 1].nil? || @columns[token.column_number + difference + 1].slots[token.row_number + difference + 1].symbol != token.symbol
break
else
consecutive_matches += 1
end
end
consecutive_matches
end
end
require 'rspec'
require 'player'
require 'token'
require 'column'
require 'board'
describe Board do
it 'is initialized with a height and width' do
board = Board.new(1, 1)
board.should be_an_instance_of Board
end
it 'has columns' do
board = Board.new(2, 1)
board.columns.length.should eq 2
end
describe 'winner?' do
it 'returns false if the token does not create a winning condition' do
board = Board.new(9, 7)
board.columns[2].add_token("&")
board.winner?(board.columns[2].slots.last, 4).should be_false
end
it 'returns true if a vertical winning condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.winner?(board.columns[0].slots.last, 4).should be_true
end
it 'returns true if a left horizontal winning condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[1].add_token("&")
board.columns[2].add_token("&")
board.columns[3].add_token("&")
board.winner?(board.columns[3].slots.last, 4).should be_true
end
it 'returns true if a right horizontal winning condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[1].add_token("&")
board.columns[2].add_token("&")
board.columns[3].add_token("&")
board.winner?(board.columns[0].slots.last, 4).should be_true
end
it 'returns true if a middle horizontal winning condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[1].add_token("&")
board.columns[2].add_token("&")
board.columns[3].add_token("&")
board.winner?(board.columns[2].slots.last, 4).should be_true
end
it 'returns false if a horizontal winning condition does not exist' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[0].add_token("#")
board.columns[1].add_token("&")
board.winner?(board.columns[1].slots.last, 4).should be_false
end
it 'returns false if there is not a horizontal winning condition' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[1].add_token("&")
board.columns[2].add_token("$")
board.columns[3].add_token("&")
board.winner?(board.columns[3].slots.last, 4).should be_false
end
it 'handles empty spaces in a horizontal row' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[1].add_token("&")
board.columns[3].add_token("&")
board.columns[4].add_token("&")
board.winner?(board.columns[3].slots.last, 4).should be_false
end
it 'returns true if negative slope condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("^")
board.columns[1].add_token("&")
board.columns[1].add_token("&")
board.columns[1].add_token("^")
board.columns[2].add_token("&")
board.columns[2].add_token("^")
board.columns[3].add_token("^")
board.winner?(board.columns[3].slots.last, 4).should be_true
end
it 'returns true if negative slope condition exists' do
board = Board.new(9, 7)
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("&")
board.columns[0].add_token("^")
board.columns[1].add_token("&")
board.columns[1].add_token("&")
board.columns[1].add_token("^")
board.columns[2].add_token("&")
board.columns[2].add_token("^")
board.columns[3].add_token("^")
board.winner?(board.columns[0].slots.last, 4).should be_true
end
it 'returns true if positive slope condition exists' do
board = Board.new(9, 7)
board.columns[3].add_token("&")
board.columns[3].add_token("&")
board.columns[3].add_token("&")
board.columns[3].add_token("^")
board.columns[2].add_token("&")
board.columns[2].add_token("&")
board.columns[2].add_token("^")
board.columns[1].add_token("&")
board.columns[1].add_token("^")
board.columns[0].add_token("^")
board.winner?(board.columns[3].slots.last, 4).should be_true
end
it 'returns true if positive slope condition exists' do
board = Board.new(9, 7)
board.columns[3].add_token("&")
board.columns[3].add_token("&")
board.columns[3].add_token("&")
board.columns[3].add_token("^")
board.columns[2].add_token("&")
board.columns[2].add_token("&")
board.columns[2].add_token("^")
board.columns[1].add_token("&")
board.columns[1].add_token("^")
board.columns[0].add_token("^")
board.winner?(board.columns[0].slots.last, 4).should be_true
end
end
# describe 'get_vertical_neighbors' do
# it 'gets all the neighbors of a token that are in the same column and within three spaces' do
# board = Board.new(9, 7)
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.get_vertical_neighbors(board.columns[0].slots.last).should eq board.columns[0].slots
# end
# end
# describe 'connect_winner?' do
# it 'is false if the array does not have enough matching tokens' do
# board = Board.new(9, 7)
# board.columns[0].add_token("$")
# board.connect_winner?(4, board.get_vertical_neighbors(board.columns[0].slots.last)).should eq false
# end
# it 'is true if the array has enough consecutive matching tokens' do
# board = Board.new(9, 7)
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.columns[0].add_token("&")
# board.connect_winner?(board.columns[0].slots.last, board.get_vertical_neighbors(board.columns[0].slots.last)).should eq true
# end
# end
end
class Column
attr_reader :position, :max_height, :slots
def initialize(position, max_height)
@position = position
@max_height = max_height
@slots = []
end
def add_token(symbol)
token = Token.new(@position, @slots.length, symbol)
slots << token
end
def full?
@slots.length == max_height
end
end
require 'rspec'
require 'token'
require 'player'
require 'column'
describe Column do
it "is initialized with a position argument and a max height argument" do
column = Column.new(1, 7)
column.should be_an_instance_of Column
end
it "has a position" do
column = Column.new(7, 7)
column.position.should eq 7
end
it "has a max height" do
column = Column.new(7, 6)
column.max_height.should eq 6
end
it "has a a collection of slots" do
column = Column.new(2, 5)
column.slots.should eq []
end
describe 'add_token' do
it "adds a token to slots" do
column = Column.new(2, 5)
column.add_token("&")
column.slots.first.should be_an_instance_of Token
end
it "adds a token to the end of slots" do
column = Column.new(2, 5)
column.add_token("&")
column.add_token("$")
column.slots.last.symbol.should eq "$"
end
end
describe 'full?' do
it 'is false if slots has not reached the max height' do
column = Column.new(2, 5)
column.full?.should eq false
end
it 'is true if the slots has reached the maximum' do
column = Column.new(2, 2)
column.add_token("&")
column.add_token("$")
column.full?.should eq true
end
end
end
require './lib/game'
require './lib/board'
require './lib/player'
require './lib/column'
require './lib/token'
def init
puts "Welcome to Connect Four! How many people are playing?"
players = gets.chomp.to_i
@game = Game.new(players, 9, 7)
end
def main
draw_board
puts "\nSelect a column you would like to drop a token into"
column_choice = player_choice
if @game.board.winner?(@game.board.columns[column_choice - 1].slots.last, 4)
draw_board
puts "#{@game.current_player.symbol} has won!!!"
else
@game.switch_player
main
end
end
def player_choice
column_choice = gets.to_i
if !([email protected]).include?(column_choice) || @game.board.columns[column_choice - 1].full?
puts "\nPlease select a different column."
player_choice
else
@game.board.columns[column_choice - 1].add_token(@game.current_player.symbol)
end
column_choice
end
def draw_board
@game.board.height.times do |row_diff|
print "|"
@game.board.columns.each do |column|
if column.slots[@game.board.height - row_diff - 1].nil?
print " "
else
print column.slots[@game.board.height - row_diff - 1].symbol
end
print " | "
end
puts ""
puts "====" * @game.board.width
end
@game.board.width.times do |time|
print " #{time + 1} "
end
puts ""
end
init
main
class Game
attr_reader :players, :board, :current_player
def initialize(players, width, height)
@possible_symbols = ["$", "*", "%", "&"]
@players = []
players.times do
player = Player.new(@possible_symbols.pop)
@players << player
end
@current_player = @players.first
@board = Board.new(width, height)
end
def switch_player
@players.rotate!
@current_player = @players.first
end
end
require 'rspec'
require 'game'
require 'board'
require 'player'
require 'column'
require 'token'
describe Game do
it 'initializes with a number of players, board height and width' do
game = Game.new(2, 9, 7)
game.should be_an_instance_of Game
end
it 'has players' do
game = Game.new(2, 9, 7)
game.players.length.should eq 2
end
it 'has a board' do
game = Game.new(2, 9, 7)
game.board.should be_an_instance_of Board
end
it 'has a current player' do
game = Game.new(2, 9, 7)
game.current_player.should eq game.players[0]
end
describe 'switch_player' do
it 'switches the current player' do
game = Game.new(2, 9, 7)
game.switch_player
game.current_player.symbol.should eq "%"
end
end
end
class Player
attr_reader :symbol, :name
def initialize(symbol)
@symbol = symbol
end
def set_name(name)
@name = name
end
end
require 'rspec'
require 'token'
require 'player'
require 'column'
describe Player do
it "is initialized with a symbol argument" do
player = Player.new("$")
player.should be_an_instance_of Player
end
it "has a symbol" do
player = Player.new("~")
player.symbol.should eq "~"
end
describe "set_name" do
it "sets the name of the player" do
player = Player.new("&")
player.set_name("Frank")
player.name.should eq "Frank"
end
end
end
class Token
attr_reader :column_number, :row_number, :symbol
def initialize(column_number=nil, row_number=nil, symbol=nil)
@column_number = column_number
@row_number = row_number
@symbol = symbol
end
end
require 'rspec'
require 'token'
require 'column'
require 'player'
describe Token do
it 'is an instance of the Token class' do
token = Token.new
token.should be_an_instance_of Token
end
it 'has an x coordinate' do
token = Token.new(2)
token.column_number.should eq 2
end
it 'has a row number' do
token = Token.new(2, 1)
token.row_number.should eq 1
end
it 'has a symbol' do
token = Token.new(1, 1, "&")
token.symbol.should eq "&"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment